diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..00a51af --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6cefc8a --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/gradle,java,intellij +# Edit at https://www.toptal.com/developers/gitignore?templates=gradle,java,intellij + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### Gradle ### +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# End of https://www.toptal.com/developers/gitignore/api/gradle,java,intellij \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 443b5d2..fb7f4a8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml index 30bab2a..d8e9561 100644 --- a/.idea/discord.xml +++ b/.idea/discord.xml @@ -1,7 +1,7 @@ - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..601f39c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..fdc392f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries-with-intellij-classes.xml b/.idea/libraries-with-intellij-classes.xml new file mode 100644 index 0000000..9fa3156 --- /dev/null +++ b/.idea/libraries-with-intellij-classes.xml @@ -0,0 +1,65 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ed3727..5821b2f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..5345a80 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,37 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java application project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/7.1/userguide/building_java_projects.html + */ + +plugins { + // Apply the application plugin to add support for building a CLI application in Java. + id 'application' +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use JUnit Jupiter for testing. + testImplementation 'org.junit.jupiter:junit-jupiter:5.7.1' + + // This dependency is used by the application. + implementation 'com.google.guava:guava:30.1-jre' + // Use gson to serialize/deserialize json files + implementation 'com.google.code.gson:gson:2.9.0' +} + +application { + // Define the main class for the application. + mainClass = 'lightcontainer.App' +} + +tasks.named('test') { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/app/src/main/java/lightcontainer/App.java b/app/src/main/java/lightcontainer/App.java new file mode 100644 index 0000000..2379664 --- /dev/null +++ b/app/src/main/java/lightcontainer/App.java @@ -0,0 +1,86 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package lightcontainer; + +import lightcontainer.domains.server.MulticastServerListener; +import lightcontainer.domains.server.UnicastServerListener; +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.protocol.rules.reader.*; +import lightcontainer.protocol.rules.writer.*; +import lightcontainer.repository.ClientHandlerRepository; +import lightcontainer.repository.FileFrontEnd; +import lightcontainer.repository.ProtocolRepositoryImpl; +import lightcontainer.repository.StoreProcessorRepository; + +public class App { + // Constant config server + // -- Unicast client port + private static final int UNICAST_PORT = 8000; + // -- Multicast listener ip, port + private static final String MULTICAST_IP = "226.66.66.1"; + private static final int MULTICAST_PORT = 15502; + + + /* + private static Thread t1 = null; + + public static void main(String[] args) throws InterruptedException { + t1 = new Thread(new Runnable() { + @Override + public void run() { + int i = 0; + while (true) { + System.out.println("HEY " + i++); + + try { + synchronized (t1) { + System.out.println("SUSPEND"); + t1.wait(); + } + } catch (InterruptedException e) {} + } + } + }); + t1.start(); + System.out.println("START"); + Thread.sleep(1000); + synchronized (t1) { + t1.notify(); + } + } + */ + + public static void main(String[] args) throws InterruptedException { + // Create all repository + ClientHandlerRepository clientRep = new ClientHandlerRepository(); + StoreProcessorRepository storeRep = new StoreProcessorRepository(); + ProtocolRepository protocolRep = new ProtocolRepositoryImpl(); + + protocolRep.addReader(new HelloRule()); + protocolRep.addReader(new SigninRule(protocolRep)); + protocolRep.addReader(new FilelistRule(protocolRep)); + protocolRep.addReader(new SavefileRule(protocolRep)); + protocolRep.addReader(new SendOkRule(protocolRep)); + + protocolRep.addWriter(new SignOkRule()); + protocolRep.addWriter(new SignErrorRule()); + protocolRep.addWriter(new SignoutRule()); + protocolRep.addWriter(new FilesRule()); + protocolRep.addWriter(new SaveFileOkRule()); + protocolRep.addWriter(new SaveFileErrorRule()); + protocolRep.addWriter(new SendfileRule()); + + FileFrontEnd ffe = new FileFrontEnd(clientRep, storeRep, protocolRep); + new UnicastServerListener(ffe, clientRep, protocolRep, UNICAST_PORT); + new MulticastServerListener(ffe, storeRep, protocolRep, MULTICAST_IP, MULTICAST_PORT); + + // close repo et client et server. + + // Thread.sleep(60000); + + // clientRep.close(); + // storeRep.close(); + } + +} diff --git a/app/src/main/java/lightcontainer/domains/Task.java b/app/src/main/java/lightcontainer/domains/Task.java new file mode 100644 index 0000000..6bce8eb --- /dev/null +++ b/app/src/main/java/lightcontainer/domains/Task.java @@ -0,0 +1,52 @@ +package lightcontainer.domains; + +import lightcontainer.enumerations.TaskStatus; +import lightcontainer.enumerations.TaskType; +import lightcontainer.protocol.ProtocolWriter; + +public class Task { + // Variables + private TaskStatus status; + private ProtocolWriter.ProtocolResult command; + private String client; + private String storeDomain; + + public Task(TaskStatus status, ProtocolWriter.ProtocolResult command, String client) { + this.status = status; + this.command = command; + this.client = client; + } + + public static Task newInstance(ProtocolWriter.ProtocolResult command, String client) { + Task task = new Task(TaskStatus.PENDING, command, client); + return task; + } + + /** + * Permet de savoir si la réponse est destinée au client + * @param storeDomain Nom du store back end fournissant la réponse. + * @return TRUE si le client doit recevoir cette réponse. + */ + public boolean isResponseOfClient(String storeDomain) { + return (status == TaskStatus.PROCESSING && this.storeDomain.equals(storeDomain)); + } + + /** + * Permet de récupérer le login du client associé à la tâche + * @return Login du client + */ + public String getClient() { + return client; + } + + public ProtocolWriter.ProtocolResult getCommand() { + return this.command; + } + + public void setDomain(String storeDomain) { + this.storeDomain = storeDomain; + if (storeDomain != null) { + this.status = TaskStatus.PROCESSING; + } + } +} diff --git a/app/src/main/java/lightcontainer/domains/client/ClientHandler.java b/app/src/main/java/lightcontainer/domains/client/ClientHandler.java new file mode 100644 index 0000000..15f1ed5 --- /dev/null +++ b/app/src/main/java/lightcontainer/domains/client/ClientHandler.java @@ -0,0 +1,187 @@ +package lightcontainer.domains.client; + +import lightcontainer.interfaces.ClientHandlerFFE; +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.protocol.ProtocolReader; +import lightcontainer.protocol.ProtocolWriter; +import lightcontainer.protocol.rules.reader.SigninRule; +import lightcontainer.protocol.rules.writer.SignErrorRule; +import lightcontainer.protocol.rules.writer.SignOkRule; + +import java.io.*; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +/** + * ClientHandler + * + * UNICAST CLIENT + * Class communicating with the client, and + * intercepting and sending files to the client. + * + * @version 1.1 + * @since 1.0 + * + * @see Runnable + * @see AutoCloseable + * @author Jérémi NIHART + */ +public class ClientHandler implements Runnable, AutoCloseable { + // Variables + private ClientHandlerFFE fileFrontEnd; + private final Socket client; + private ProtocolRepository protocolRep; + private boolean client_run; + + /** + * Login of client. + * State : NULL if not connected and Login string if connected + */ + private String login; + + private BufferedReader reader; + private PrintWriter writer; + private ProtocolWriter.ProtocolResult response; + + // Constructor + public ClientHandler(Socket client, ClientHandlerFFE ffe, ProtocolRepository protocolRep) { + this.fileFrontEnd = ffe; + this.client = client; + this.protocolRep = protocolRep; + this.client_run = false; + initClient(); + } + + /** + * Initialise the Client's Reader and Writer. + * + * @since 1.0 + * + * @see BufferedReader + * @see PrintWriter + */ + private void initClient() { + try { + this.reader = new BufferedReader(new InputStreamReader( + this.client.getInputStream(), + StandardCharsets.UTF_8 + )); + this.writer = new PrintWriter(new OutputStreamWriter( + this.client.getOutputStream(), + StandardCharsets.UTF_8 + ), true); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Thread Function + * Start the dialogue with the client. + * + * @since 1.0 + */ + @Override + public void run() { + this.client_run = true; + while (this.client_run) { + try { + String command = this.reader.readLine(); + if (command != null) System.out.println("Client: " + command); + + ProtocolReader.ProtocolResult ruleResult = protocolRep.executeReader(command + "\r\n"); + + if (isConnected()) { + ruleResult.read( + this.client.getInputStream() + ); + ProtocolWriter.ProtocolResult writerCommand = ruleResult.getResultCommand(); + + if (ruleResult.getReceiver() == ProtocolReader.ResultCmdReceiver.STOREBACKEND) { + fileFrontEnd.newCommand( + writerCommand, + getLogin()); // Envoie dans la file de tâche FileFrontEnd en attente d'un traitement d'un StorBackEnd + + // Attend la fin de la réalisation de la tâche + waitTaskResponse(); + + writer.write(response.getCommand()); // Renvoye au client + writer.flush(); + response.write(this.client.getOutputStream()); // Ecrit au client si nécessaire + } else { + writer.write(writerCommand.getCommand()); // Renvoye au client + writer.flush(); + } + + } else { + authentication(ruleResult); + } + } catch (IOException ignore) { } + + } + } + + /** + * Permet au Client d'attendre la fin de la réalisation de sa tâche + */ + private void waitTaskResponse() { + synchronized (this) { + try { + this.wait(); + } catch (InterruptedException e) { e.printStackTrace(); } + } + } + + public void respond(ProtocolWriter.ProtocolResult response) { + this.response = response; + synchronized (this) { + this.notify(); + } + } + + private void authentication(ProtocolReader.ProtocolResult ruleResult) { + try { + SigninRule.Result signinResult = (SigninRule.Result) ruleResult; + if (signinResult.checkCredentials()) { + this.login = signinResult.getLogin(); + ProtocolWriter.ProtocolResult signokResult = protocolRep.executeWriter(SignOkRule.NAME); + writer.write(signokResult.getCommand()); + writer.flush(); + return; + } + } catch (ClassCastException ignored) {} + + ProtocolWriter.ProtocolResult signErrorResult = protocolRep.executeWriter(SignErrorRule.NAME); + writer.write(signErrorResult.getCommand()); // Envoie SignError car echec de la connection + writer.flush(); + this.close(); // Fermeture de la connection + } + + /** + * Permet de savoir si l'utilisateur s'est connecté (Avec login et mdp) + * @return + */ + private boolean isConnected() { + return login != null; + } + + /** + * AutoClosable Function + * Close the Client thread and resources. + * + * @since 1.0 + */ + @Override + public void close() { + if (this.client_run) { + try { + this.client_run = false; + this.client.close(); + } catch (IOException ignored) { } + } + } + + public String getLogin() { + return this.login; + } +} diff --git a/app/src/main/java/lightcontainer/domains/client/StoreProcessor.java b/app/src/main/java/lightcontainer/domains/client/StoreProcessor.java new file mode 100644 index 0000000..642cdd8 --- /dev/null +++ b/app/src/main/java/lightcontainer/domains/client/StoreProcessor.java @@ -0,0 +1,185 @@ +package lightcontainer.domains.client; + +import lightcontainer.domains.Task; +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.interfaces.StoreProcessorFFE; +import lightcontainer.protocol.ProtocolReader; +import lightcontainer.protocol.ProtocolWriter; + +import java.io.*; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * StoreProcessor + * + * MULTICAST CLIENT + * Class communicating with the storebackend and + * processing the tasks in the FileFrontEnd + * + * @version 1.1 + * @since 1.0 + * + * @see Runnable + * @see AutoCloseable + * @author Jérémi NIHART + */ +public class StoreProcessor extends Thread implements AutoCloseable { + // Variables + private final StoreProcessorFFE fileFrontEnd; + private final Socket store; + private final String domain; + private boolean client_run; + + private BufferedReader reader; + private PrintWriter writer; + private ProtocolWriter.ProtocolResult protocolResult; + private ProtocolRepository protocolRep; + + // Constructor + public StoreProcessor(Socket socket, String domain, StoreProcessorFFE ffe, ProtocolRepository protocolRep) { + this.domain = domain; + this.fileFrontEnd = ffe; + this.store = socket; + this.protocolRep = protocolRep; + this.client_run = false; + initStore(); + } + + /** + * Initialise the Store's Reader and Writer. + * + * @see BufferedReader + * @see PrintWriter + * @since 1.0 + */ + private void initStore() { + try { + this.reader = new BufferedReader(new InputStreamReader( + this.store.getInputStream(), + StandardCharsets.UTF_8 + )); + this.writer = new PrintWriter(new OutputStreamWriter( + this.store.getOutputStream(), + StandardCharsets.UTF_8 + ), true); + + this.start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Thread Function + * Start the dialogue with the storebackend. + * + * @since 1.0 + */ + @Override + public void run() { + this.client_run = true; + while (this.client_run) { + try { + waitAction(); + System.out.println("[SBE] Envoie commande : " + protocolResult.getCommand()); + + // Request + this.writer.write(protocolResult.getCommand()); + this.writer.flush(); + protocolResult.write(this.store.getOutputStream()); + + + // Response + String responseCommand = this.reader.readLine() + "\r\n"; + if (responseCommand != null) + System.out.println("StoreBackEnd: " + responseCommand); + ProtocolReader.ProtocolResult responseResult = protocolRep.executeReader(responseCommand); + responseResult.read( + this.store.getInputStream() + ); + System.out.println("StoreBackEnd response to client: " + responseResult.getResultCommand()); + + alertAvalaible(responseResult.getResultCommand()); + + } catch (IOException ignore) { } + } + } + + /** + * Permet de demander au StoreBackEnd d'effectuer une commande + * @param protocolResult La commande à effectuer + */ + public void executeCommand(ProtocolWriter.ProtocolResult protocolResult) { + synchronized (this) { + this.protocolResult = protocolResult; + this.notify(); + } + } + + /** + * Permet de déclarer le StoreBackEnd disponible + */ + private void alertAvalaible(ProtocolWriter.ProtocolResult responseCommand) { + synchronized (this) { + this.protocolResult = null; + fileFrontEnd.onStoreAvailable(this, responseCommand); + waitAction(); + } + } + + /** + * Permet au Store d'attendre une action à réaliser + */ + private void waitAction() { + synchronized (this) { + try { + this.wait(); + } catch (InterruptedException e) { e.printStackTrace(); } + } + } + + /** + * AutoClosable Function + * Close the Storage thread and resources. + * + * @since 1.0 + */ + @Override + public void close() { + if (this.client_run) { + try { + this.client_run = false; + this.store.close(); + } catch (IOException ignored) { } + } + } + + /** + * Compare two StoreProcessor object to check if equals. + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StoreProcessor that = (StoreProcessor) o; + return Objects.equals(domain, that.domain); + } + + /** + * Get hash code + */ + @Override + public int hashCode() { + return Objects.hash(domain); + } + + public boolean canProcessTask(Task task) { + return this.protocolResult == null; // Vérifier si tâche veut ce SBE + } + + public String getDomain() { + return this.domain; + } +} diff --git a/app/src/main/java/lightcontainer/domains/server/MulticastServerListener.java b/app/src/main/java/lightcontainer/domains/server/MulticastServerListener.java new file mode 100644 index 0000000..868ada1 --- /dev/null +++ b/app/src/main/java/lightcontainer/domains/server/MulticastServerListener.java @@ -0,0 +1,97 @@ +package lightcontainer.domains.server; + +import lightcontainer.domains.client.StoreProcessor; +import lightcontainer.interfaces.MulticastSPR; +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.protocol.ProtocolReader; +import lightcontainer.protocol.rules.reader.HelloRule; +import lightcontainer.repository.FileFrontEnd; + +import java.io.IOException; +import java.net.*; + +/** + * StoreMulticastRunnable + *

+ * Class listening to the announcement of new StoreBackEnd. + * Allowing it to be used as a storage unit. + * + * @author Jérémi NIHART + * @version 1.0 + * @see Runnable + * @since 1.0 + */ +public class MulticastServerListener implements Runnable { + // Variable + private final String multicast_address; + private final int multicast_port; + private FileFrontEnd ffe; + private final MulticastSPR repository; + private final ProtocolRepository protocolRep; + + private final byte[] buffer = new byte[256]; + private MulticastSocket listener; + + // Constructor + public MulticastServerListener(FileFrontEnd ffe, MulticastSPR repository, ProtocolRepository protocolRep, String multicast_address, int multicast_port) { + this.ffe = ffe; + this.repository = repository; + this.protocolRep = protocolRep; + this.multicast_address = multicast_address; + this.multicast_port = multicast_port; + repository.setServerListener(this); + } + + /** + * Start Multicast listening on indicated port and IP group. + * + * @see MulticastSocket#receive(DatagramPacket) + * @see DatagramPacket + * @since 1.0 + */ + @Override + public void run() { + try { + // Create a new listening socket + this.listener = new MulticastSocket(this.multicast_port); + // Create an identifier for the multicast group on the specified ip + InetAddress listener_group = InetAddress.getByName(this.multicast_address); + // Creation of a packet for the information received + DatagramPacket packet = new DatagramPacket(this.buffer, this.buffer.length); + // Add the listener to the multicast group + this.listener.joinGroup(listener_group); + while (true) { + // Read the packet received and build a string of characters + this.listener.receive(packet); + String data = new String(packet.getData(), 0, packet.getLength()); + // Create a new StoreBacked (try used in the case of an error to maintain the listening loop) + try { + // TODO Récupérer le port du message du packet et le setup (add description of the line). + HelloRule.Result readerResult = protocolRep.executeReader(data); + System.out.printf("Nouveau SBE : Domain=%s | Port=%d\n", readerResult.getDomain(), readerResult.getPort()); + + Socket socket = new Socket(packet.getAddress(), readerResult.getPort()); + + // Create the store processor + StoreProcessor storeProcessor = new StoreProcessor(socket, readerResult.getDomain(), ffe, protocolRep); // TODO : Voir comment on procède get via repo ou ici ?! + + // Add the store processor to its repository + this.repository.addStore(storeProcessor); + } catch (IOException ignore) { + ignore.printStackTrace(); + } + } + } catch (Exception ignore) { + } + } + + /** + * Closes the MulticastSocket connection and aborts the listening and infinite listening loop. + * + * @see MulticastServerListener#run() + * @since 1.0 + */ + public void stop() { + this.listener.close(); + } +} diff --git a/app/src/main/java/lightcontainer/domains/server/UnicastServerListener.java b/app/src/main/java/lightcontainer/domains/server/UnicastServerListener.java new file mode 100644 index 0000000..5970d61 --- /dev/null +++ b/app/src/main/java/lightcontainer/domains/server/UnicastServerListener.java @@ -0,0 +1,75 @@ +package lightcontainer.domains.server; + +import lightcontainer.domains.client.ClientHandler; +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.interfaces.UnicastCHR; +import lightcontainer.repository.FileFrontEnd; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +public class UnicastServerListener implements Runnable { + // Variables + private ServerSocket server; + private FileFrontEnd ffe; + private final UnicastCHR repository; + private ProtocolRepository protocolRep; + private final int server_port; + private boolean server_run; + + // Constructor + public UnicastServerListener(FileFrontEnd ffe, UnicastCHR repository, ProtocolRepository protocolRep, int port) { + this.ffe = ffe; + this.repository = repository; + this.protocolRep = protocolRep; + this.server_port = port; + this.server_run = false; + repository.setServerListener(this); + } + + /** + * Initializes the server and starts it on the previously selected port. + * + * @since 1.0 + * + * @see Thread#start() + * @see ClientHandler + */ + @Override + public void run() { + try { + // Allow looping in the loop and create a socket server + this.server_run = true; + this.server = new ServerSocket(this.server_port); + while (this.server_run) { + // Accepting connection requests (blocking) + Socket client = this.server.accept(); + System.out.println("New Client"); + // Create a new Handler client by passing these dependencies to it + ClientHandler clientHandler = new ClientHandler(client, ffe, protocolRep); // TODO passer FileFrontEnd ou faire ca dans le repository ?! + // Add the client handler to its repository (clienthandlerrepository) + this.repository.addClient(clientHandler); + // Start the thread + (new Thread(clientHandler)).start(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Stops the server and terminates the new connection. + * + * @since 1.0 + */ + public void stop() { + if (this.server_run) { + try { + this.server_run = false; + this.server.close(); + } catch (IOException ignored) { } + } + } + +} diff --git a/app/src/main/java/lightcontainer/enumerations/TaskStatus.java b/app/src/main/java/lightcontainer/enumerations/TaskStatus.java new file mode 100644 index 0000000..12200e6 --- /dev/null +++ b/app/src/main/java/lightcontainer/enumerations/TaskStatus.java @@ -0,0 +1,27 @@ +package lightcontainer.enumerations; + +import lightcontainer.domains.Task; + +/** + * Enumeration defining the status of a {@link Task}. + */ +public enum TaskStatus { + + /** + * En attente d'être traitée + */ + PENDING, + + /** + * En train d'être traitée + */ + PROCESSING, + + /** + * Une erreur est survenue + */ + ERROR, + + + SUCCESS +} diff --git a/app/src/main/java/lightcontainer/enumerations/TaskType.java b/app/src/main/java/lightcontainer/enumerations/TaskType.java new file mode 100644 index 0000000..347e6ce --- /dev/null +++ b/app/src/main/java/lightcontainer/enumerations/TaskType.java @@ -0,0 +1,12 @@ +package lightcontainer.enumerations; + +import lightcontainer.domains.Task; + +/** + * enumeration to define the type of a {@link Task}. + */ +public enum TaskType { + SEND, + RECEIVE, + DELETE +} diff --git a/app/src/main/java/lightcontainer/interfaces/ClientHandlerFFE.java b/app/src/main/java/lightcontainer/interfaces/ClientHandlerFFE.java new file mode 100644 index 0000000..3f1ca8c --- /dev/null +++ b/app/src/main/java/lightcontainer/interfaces/ClientHandlerFFE.java @@ -0,0 +1,20 @@ +package lightcontainer.interfaces; + +import lightcontainer.domains.client.ClientHandler; +import lightcontainer.protocol.ProtocolWriter; +import lightcontainer.repository.FileFrontEnd; + +/** + * A communication interface between a {@link ClientHandler} and the {@link FileFrontEnd}. + */ +public interface ClientHandlerFFE { + // functions + + /** + * Demande le traitement d'une commande + * @param command Commande à traiter + * @param client identifiant du client à qui est affilié cette commande + */ + void newCommand(ProtocolWriter.ProtocolResult command, String client); + +} diff --git a/app/src/main/java/lightcontainer/interfaces/MulticastSPR.java b/app/src/main/java/lightcontainer/interfaces/MulticastSPR.java new file mode 100644 index 0000000..1d634c4 --- /dev/null +++ b/app/src/main/java/lightcontainer/interfaces/MulticastSPR.java @@ -0,0 +1,27 @@ +package lightcontainer.interfaces; + +import lightcontainer.domains.Task; +import lightcontainer.domains.client.StoreProcessor; +import lightcontainer.domains.server.MulticastServerListener; +import lightcontainer.repository.StoreProcessorRepository; + +/** + * A communication interface between a {@link StoreProcessor} and the {@link StoreProcessorRepository}. + */ +public interface MulticastSPR { + /** + * Setter, allow to define the ServerListener of a repository. + * @param server ServerListener to set as default. + */ + void setServerListener(MulticastServerListener server); + + /** + * Add a StorePorcessor. + * @param store Store processor to add. + */ + void addStore(StoreProcessor store); + + String findDomain(Task task); + + void assignTask(String stor, Task task); +} diff --git a/app/src/main/java/lightcontainer/interfaces/ProtocolRepository.java b/app/src/main/java/lightcontainer/interfaces/ProtocolRepository.java new file mode 100644 index 0000000..1f9f48f --- /dev/null +++ b/app/src/main/java/lightcontainer/interfaces/ProtocolRepository.java @@ -0,0 +1,15 @@ +package lightcontainer.interfaces; + +import lightcontainer.protocol.ProtocolReader; +import lightcontainer.protocol.ProtocolWriter; + +public interface ProtocolRepository { + + T executeReader(String data); + + T executeWriter(String cmdName, String... data); + + void addReader(ProtocolReader reader); + + void addWriter(ProtocolWriter writer); +} diff --git a/app/src/main/java/lightcontainer/interfaces/StoreProcessorFFE.java b/app/src/main/java/lightcontainer/interfaces/StoreProcessorFFE.java new file mode 100644 index 0000000..26be68b --- /dev/null +++ b/app/src/main/java/lightcontainer/interfaces/StoreProcessorFFE.java @@ -0,0 +1,17 @@ +package lightcontainer.interfaces; + +import lightcontainer.domains.client.StoreProcessor; +import lightcontainer.protocol.ProtocolWriter; +import lightcontainer.repository.FileFrontEnd; + +/** + * A communication interface between a {@link StoreProcessor} and the {@link FileFrontEnd}. + */ +public interface StoreProcessorFFE { + /** + * Allows a {@link StoreProcessor} to notify the FFE that it's available. + * @param store The store processor that is now available. + * @param responseCommand + */ + void onStoreAvailable(StoreProcessor store, ProtocolWriter.ProtocolResult response); +} diff --git a/app/src/main/java/lightcontainer/interfaces/UnicastCHR.java b/app/src/main/java/lightcontainer/interfaces/UnicastCHR.java new file mode 100644 index 0000000..160b533 --- /dev/null +++ b/app/src/main/java/lightcontainer/interfaces/UnicastCHR.java @@ -0,0 +1,26 @@ +package lightcontainer.interfaces; + +import lightcontainer.domains.client.ClientHandler; +import lightcontainer.domains.server.MulticastServerListener; +import lightcontainer.domains.server.UnicastServerListener; +import lightcontainer.protocol.ProtocolWriter; +import lightcontainer.repository.ClientHandlerRepository; + +/** + * A communication interface between a {@link ClientHandler} and the {@link ClientHandlerRepository}. + */ +public interface UnicastCHR { + /** + * Setter, allow to define the ServerListener of a repository. + * @param server ServerListener to set as default. + */ + void setServerListener(UnicastServerListener server); + + /** + * Add a ClientHandler. + * @param client Client Handler to add. + */ + void addClient(ClientHandler client); + + void respondToClient(String client, ProtocolWriter.ProtocolResult response); +} diff --git a/app/src/main/java/lightcontainer/protocol/ProtocolReader.java b/app/src/main/java/lightcontainer/protocol/ProtocolReader.java new file mode 100644 index 0000000..6d37951 --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/ProtocolReader.java @@ -0,0 +1,92 @@ +package lightcontainer.protocol; + +import java.io.InputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class ProtocolReader { + // Variables + private final Pattern rulePattern; + + // Constructor + protected ProtocolReader(String pattern) { + this.rulePattern = Pattern.compile(pattern); + } + + public enum ResultCmdReceiver { + CLIENT, + STOREBACKEND + } + + /** + * Modèle utilisé par tout les résultats des règles de protocol. + * Lorsqu'il retourne son résultat, on vérifie si il y a une demande de lecture/écriture de fichier depuis le réseau. Si oui on appel ces méthodes, sinon on ne fait rien. + * Ensuite on regarde après l'exécution de ces méthode ou non si il y a une commande de retour ou non et l'envoyons au receiver spécifié par la commande. + */ + public class ProtocolResult { + /** + * Command qui sera renvoyée par exemple au client + */ + private ProtocolWriter.ProtocolResult resultCommand; + + /** + * Désigne vers ou cette commande est envoyée. + * ResultCmdReceiver.CLIENT : Signifie que cette commande va être directement revoyée au client. + * ResultCmdReceiver.STOREBACKEND : Signifie que cette commande va être envoyée dans la file de tâche du server et êre en attente d'envoie à un StorBackEnd + */ + private ResultCmdReceiver receiver; + + public ResultCmdReceiver getReceiver() { + return receiver; + } + + /** + * Récupérer la commande à envoyer + * @return Commande + */ + public ProtocolWriter.ProtocolResult getResultCommand() { + return this.resultCommand; + } + + /** + * Mettre la commande à envoyer + * @param resultCommand Commande à envoyer + * @param receiver Le receveur de cette commande + */ + public void setResultCommand(ProtocolWriter.ProtocolResult resultCommand, ResultCmdReceiver receiver) { + this.resultCommand = resultCommand; + this.receiver = receiver; + } + + /** + * Permet de lire un fichier. Cad reçevoir le contenu d'un fichier provenant du réseau. + * Redéfinissez cette méthode pour l'utiliser + * @param reader Buffer rempli du fichier + */ + public void read(InputStream reader) {} + } + + /** + * Permet de lancer la décomposition d'une commande pour en extraire les données + * @param data Contenu de la commande + */ + public T execute(String data) { + Matcher ruleMatcher = this.rulePattern.matcher(data); + + if (ruleMatcher.matches()) { + String[] groups = new String[ruleMatcher.groupCount()]; + + for (int i = 1; i <= groups.length; ++i) + groups[i - 1] = ruleMatcher.group(i); + + return onExecuted(groups); + } + return null; + } + + /** + * Cette méthode est appelée lors de l'exécution de la règle + * @param data Paramètres pour créer la commande. + */ + protected abstract T onExecuted(String... data); +} diff --git a/app/src/main/java/lightcontainer/protocol/ProtocolWriter.java b/app/src/main/java/lightcontainer/protocol/ProtocolWriter.java new file mode 100644 index 0000000..87f6682 --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/ProtocolWriter.java @@ -0,0 +1,79 @@ +package lightcontainer.protocol; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.StringJoiner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class ProtocolWriter { + // Variables + private final Pattern rulePattern; + private final String cmdName; + + // Constructor + protected ProtocolWriter(String cmdName, String pattern) { + this.rulePattern = Pattern.compile(pattern); + this.cmdName = cmdName; + } + + /** + * Permet de récupérer le nom de la commande. + * @return Nom de la commande. + */ + public final String getCmdName() { + return cmdName; + } + + public static class ProtocolResult { + + private String command; + + public String getCommand() { + return command; + } + + private void setCommand(String command) { + this.command = command; + } + + /** + * Permet d'écrire un fichier sur le réseau. Cad envoyer le contenu d'un fichier sur le réseau. + * Redéfinissez cette méthode pour l'utiliser. + * @param writer Buffer à remplir qui sera envoyer via le réseau + */ + public void write(OutputStream writer) {} + } + + /** + * Permet de contruire une commande selon une règle établie. + * @param data Les données à ajouter dans la commande; L'ordre défini leur position dans la commande + * @return La commande construite + */ + public final T execute(String... data) { + // Concatatène le nom de la commande avec les données (trim), avec un espace entre chaque + StringBuilder builder = new StringBuilder(this.cmdName); + + for (String param : data) + builder.append(" " + param); + + String command = builder + "\r\n"; + Matcher ruleMatcher = this.rulePattern.matcher(command); // Vérifie que tout match (cf. Matcher). Si match alors on retourne la commande build, sinon on retourne NULL + + if (ruleMatcher.matches()) { + ProtocolResult result = onExecuted(data); + result.setCommand(command); + return (T) result; + } + + return null; + } + + /** + * Cette méthode est appelée lors de l'exécution de la règle + */ + protected T onExecuted(String... data) { + return (T) new ProtocolResult(); + } + +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/reader/FilelistRule.java b/app/src/main/java/lightcontainer/protocol/rules/reader/FilelistRule.java new file mode 100644 index 0000000..3b292be --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/reader/FilelistRule.java @@ -0,0 +1,33 @@ +package lightcontainer.protocol.rules.reader; + +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.protocol.ProtocolReader; +import lightcontainer.protocol.ProtocolWriter; +import lightcontainer.protocol.rules.writer.FilesRule; +import lightcontainer.protocol.rules.writer.SignOkRule; + +public class FilelistRule extends ProtocolReader { + // Constants + private static final String PATTERN = "^FILELIST\r\n$"; + + private ProtocolRepository protocolRep; + + // Constructor + public FilelistRule(ProtocolRepository protocolRep) { + super(PATTERN); + this.protocolRep = protocolRep; + } + + public class Result extends ProtocolResult { } + + /** + * Cette méthode est appelée lors de l'exécution de la règle + * @param data Paramètres pour créer la commande. + */ + @Override + protected FilelistRule.Result onExecuted(String... data) { + FilelistRule.Result result = new Result(); + result.setResultCommand(this.protocolRep.executeWriter(FilesRule.NAME, "endbenja.txt!500"), ResultCmdReceiver.CLIENT); + return result; + } +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/reader/HelloRule.java b/app/src/main/java/lightcontainer/protocol/rules/reader/HelloRule.java new file mode 100644 index 0000000..407e2dc --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/reader/HelloRule.java @@ -0,0 +1,54 @@ +package lightcontainer.protocol.rules.reader; + +import lightcontainer.protocol.ProtocolReader; + +import java.io.BufferedReader; +import java.util.ArrayList; +import java.util.List; + +public class HelloRule extends ProtocolReader { + + private static final String PATTERN = "^HELLO ([A-Za-z0-9]{5,20}) ([0-9]{1,5})\r\n$"; + + // Index du domain dans le tableau de donnée + private static final int DOMAIN = 0; + + //Index du port dans le tableau de donnée + private static final int PORT = 1; + + public HelloRule() { + super(PATTERN); + } + + + public class Result extends ProtocolResult { + + private final String domain; + private final int port; + + public Result(String domain, int port) { + this.domain = domain; + this.port = port; + } + + public String getDomain() { + return domain; + } + + public int getPort() { + return port; + } + + + } + + + @Override + protected HelloRule.Result onExecuted(String... data) { + String domain = data[DOMAIN]; + int port = Integer.parseInt(data[PORT]); + + return new HelloRule.Result(domain, port); + } + +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/reader/SavefileRule.java b/app/src/main/java/lightcontainer/protocol/rules/reader/SavefileRule.java new file mode 100644 index 0000000..444a916 --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/reader/SavefileRule.java @@ -0,0 +1,66 @@ +package lightcontainer.protocol.rules.reader; + +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.protocol.ProtocolReader; +import lightcontainer.protocol.rules.writer.SaveFileErrorRule; +import lightcontainer.protocol.rules.writer.SaveFileOkRule; +import lightcontainer.protocol.rules.writer.SendfileRule; +import lightcontainer.utils.FileReceiver; + +import java.io.IOException; +import java.io.InputStream; + +public class SavefileRule extends ProtocolReader { + // Constants + private static final String PATTERN = "^SAVEFILE ([^ !]{1,20}) ([0-9]{1,10})\r\n$"; + private static final int FILE_NAME = 0; // Index file name. + private static final int FILE_SIZE = 1; // Index file size. + + private ProtocolRepository protocolRep; + + // Constructor + public SavefileRule(ProtocolRepository protocolRep) { + super(PATTERN); + this.protocolRep = protocolRep; + } + + public class Result extends ProtocolResult { + // Variables + private final String filename; + private final int size; + + // Construct + public Result(String filename, int size) { + this.filename = filename; + this.size = size; + } + + @Override + public void read(InputStream reader) { + super.read(reader); + System.out.printf("Sauvegarde du fichier : %s %d\n", filename, size); + + try { + FileReceiver fileReceiver = new FileReceiver("/home/benjamin/ffe"); // "D:\\"); + if (!fileReceiver.receiveFile(reader, this.filename, this.size)) + throw new IOException(); + + this.setResultCommand(protocolRep.executeWriter(SendfileRule.NAME, this.filename, String.valueOf(this.size), "EMPREINTEBLBLBLBLBLABLABLBALBALBALBALBALBALBALBALBALABLBALBALBALABLABLABLABLABLABLABALBLABALABLABLABLABKJABKAHBHKBHJbhjvgkh"), ResultCmdReceiver.STOREBACKEND); + } catch (IOException e) { + this.setResultCommand(protocolRep.executeWriter(SaveFileErrorRule.NAME), ResultCmdReceiver.CLIENT); + e.printStackTrace(); + } + } + } + + /** + * Cette méthode est appelée lors de l'exécution de la règle + * @param data Paramètres pour créer la commande. + */ + @Override + protected SavefileRule.Result onExecuted(String... data) { + SavefileRule.Result result = new SavefileRule.Result(data[FILE_NAME], Integer.parseInt(data[FILE_SIZE])); + + return result; + } +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/reader/SendOkRule.java b/app/src/main/java/lightcontainer/protocol/rules/reader/SendOkRule.java new file mode 100644 index 0000000..8243e43 --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/reader/SendOkRule.java @@ -0,0 +1,29 @@ +package lightcontainer.protocol.rules.reader; + +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.protocol.ProtocolReader; +import lightcontainer.protocol.ProtocolWriter; +import lightcontainer.protocol.rules.writer.SaveFileOkRule; + +public class SendOkRule extends ProtocolReader { + + + // Constants + private static final String PATTERN = "^SEND_OK\r\n$"; + + private ProtocolRepository protocolRep; + + // Constructor + public SendOkRule(ProtocolRepository protocolRep) { + super(PATTERN); + this.protocolRep = protocolRep; + } + + + @Override + protected ProtocolReader.ProtocolResult onExecuted(String... data) { + ProtocolReader.ProtocolResult result = new ProtocolReader.ProtocolResult(); + result.setResultCommand(protocolRep.executeWriter(SaveFileOkRule.NAME), ResultCmdReceiver.CLIENT); + return result; + } +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/reader/SigninRule.java b/app/src/main/java/lightcontainer/protocol/rules/reader/SigninRule.java new file mode 100644 index 0000000..4e498dd --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/reader/SigninRule.java @@ -0,0 +1,65 @@ +package lightcontainer.protocol.rules.reader; + +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.protocol.ProtocolReader; +import lightcontainer.protocol.rules.writer.SignErrorRule; +import lightcontainer.protocol.rules.writer.SignOkRule; + +import java.io.InputStream; + +public class SigninRule extends ProtocolReader { + // Constants + private static final String PATTERN = "^SIGNIN ([A-Za-z0-9]{2,20}) ([^ !]{5,50})\r\n$"; + private static final int LOGIN = 0; // Index du domain dans le tableau de données + private static final int PASSWORD = 1; // Index du port dans le tableau de données + + private ProtocolRepository protocolRep; + + // Constructor + public SigninRule(ProtocolRepository protocolRep) { + super(PATTERN); + this.protocolRep = protocolRep; + } + + public class Result extends ProtocolResult { + // Variables + private final String login; + private final String password; + + // Result constructor + public Result(String login, String password) { + this.login = login; + this.password = password; + } + + public String getLogin() { + return login; + } + + public String getPassword() { + return password; + } + + public boolean checkCredentials() { + return getLogin().equals("aa") && getPassword().equals("aaaaa"); + } + + @Override + public void read(InputStream reader) { + + } + } + + @Override + protected SigninRule.Result onExecuted(String... data) { + SigninRule.Result result = new SigninRule.Result(data[LOGIN], data[PASSWORD]); + + // TODO : Création d'une règle d'écriture SIGN_OK et SIGN_ERROR proprement + if (result.checkCredentials()) { + result.setResultCommand(this.protocolRep.executeWriter(SignOkRule.NAME), ResultCmdReceiver.CLIENT); + } else { + result.setResultCommand(this.protocolRep.executeWriter(SignErrorRule.NAME), ResultCmdReceiver.CLIENT); + } + return result; + } +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/writer/FilesRule.java b/app/src/main/java/lightcontainer/protocol/rules/writer/FilesRule.java new file mode 100644 index 0000000..a611163 --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/writer/FilesRule.java @@ -0,0 +1,15 @@ +package lightcontainer.protocol.rules.writer; + +import lightcontainer.protocol.ProtocolWriter; + +public class FilesRule extends ProtocolWriter { + + private static final String PATTERN = "^FILES( ([^ !]{1,20})!([0-9]{1,10})){0,50}\r\n$"; + + public static final String NAME = "FILES"; + + public FilesRule() { + super(NAME, PATTERN); + } + +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/writer/SaveFileErrorRule.java b/app/src/main/java/lightcontainer/protocol/rules/writer/SaveFileErrorRule.java new file mode 100644 index 0000000..8dda3a7 --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/writer/SaveFileErrorRule.java @@ -0,0 +1,15 @@ +package lightcontainer.protocol.rules.writer; + +import lightcontainer.protocol.ProtocolWriter; + +public class SaveFileErrorRule extends ProtocolWriter { + + private static final String PATTERN = "^SAVEFILE_ERROR\r\n$"; + + public static final String NAME = "SAVEFILE_ERROR"; + + public SaveFileErrorRule() { + super(NAME, PATTERN); + } + +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/writer/SaveFileOkRule.java b/app/src/main/java/lightcontainer/protocol/rules/writer/SaveFileOkRule.java new file mode 100644 index 0000000..1bd90d3 --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/writer/SaveFileOkRule.java @@ -0,0 +1,15 @@ +package lightcontainer.protocol.rules.writer; + +import lightcontainer.protocol.ProtocolWriter; + +public class SaveFileOkRule extends ProtocolWriter { + + private static final String PATTERN = "^SAVEFILE_OK\r\n$"; + + public static final String NAME = "SAVEFILE_OK"; + + public SaveFileOkRule() { + super(NAME, PATTERN); + } + +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/writer/SendfileRule.java b/app/src/main/java/lightcontainer/protocol/rules/writer/SendfileRule.java new file mode 100644 index 0000000..be51736 --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/writer/SendfileRule.java @@ -0,0 +1,49 @@ +package lightcontainer.protocol.rules.writer; + +import lightcontainer.protocol.ProtocolWriter; +import lightcontainer.utils.FileReceiver; +import lightcontainer.utils.FileSender; + +import java.io.OutputStream; + +public class SendfileRule extends ProtocolWriter { + + private static final String PATTERN = "^SENDFILE [A-Za-z0-9.]{0,200} [0-9]{1,10} [A-Za-z0-9.]{50,200}\r\n$"; + + public static final String NAME = "SENDFILE"; + + private static final int HASHED_FILE_NAME = 0; // Index file name hashed. + private static final int FILE_SIZE = 1; // Index file size. + private static final int HASHED_FILE_CONTENT = 2; // Index file content hashed. + + public SendfileRule() { + super(NAME, PATTERN); + } + + public class Result extends ProtocolWriter.ProtocolResult { + + private final String hashedFileName; + private final int fileSize; + private final String hashedFileContent; + + public Result(String hashedFileName, int fileSize, String hashedFileContent) { + this.hashedFileName = hashedFileName; + this.fileSize = fileSize; + this.hashedFileContent = hashedFileContent; + } + + @Override + public void write(OutputStream writer) { + super.write(writer); + System.out.println("Envoie du fichier au SBE"); + FileSender fileSender = new FileSender("/home/benjamin/ffe"); + fileSender.sendFile(hashedFileName, writer); + } + } + + + @Override + protected SendfileRule.Result onExecuted(String... data) { + return new SendfileRule.Result(data[HASHED_FILE_NAME], Integer.parseInt(data[FILE_SIZE]), data[HASHED_FILE_CONTENT]); + } +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/writer/SignErrorRule.java b/app/src/main/java/lightcontainer/protocol/rules/writer/SignErrorRule.java new file mode 100644 index 0000000..b78a66f --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/writer/SignErrorRule.java @@ -0,0 +1,15 @@ +package lightcontainer.protocol.rules.writer; + +import lightcontainer.protocol.ProtocolWriter; + +public class SignErrorRule extends ProtocolWriter { + + private static final String PATTERN = "^SIGN_ERROR\r\n$"; + + public static final String NAME = "SIGN_ERROR"; + + public SignErrorRule() { + super(NAME, PATTERN); + } + +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/writer/SignOkRule.java b/app/src/main/java/lightcontainer/protocol/rules/writer/SignOkRule.java new file mode 100644 index 0000000..44b3f3c --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/writer/SignOkRule.java @@ -0,0 +1,14 @@ +package lightcontainer.protocol.rules.writer; + +import lightcontainer.protocol.ProtocolWriter; + +public class SignOkRule extends ProtocolWriter { + + private static final String PATTERN = "^SIGN_OK\r\n$"; + + public static final String NAME = "SIGN_OK"; + + public SignOkRule() { + super(NAME, PATTERN); + } +} diff --git a/app/src/main/java/lightcontainer/protocol/rules/writer/SignoutRule.java b/app/src/main/java/lightcontainer/protocol/rules/writer/SignoutRule.java new file mode 100644 index 0000000..ff42d2c --- /dev/null +++ b/app/src/main/java/lightcontainer/protocol/rules/writer/SignoutRule.java @@ -0,0 +1,14 @@ +package lightcontainer.protocol.rules.writer; + +import lightcontainer.protocol.ProtocolWriter; + +public class SignoutRule extends ProtocolWriter { + + private static final String PATTERN = "^SIGNOUT\r\n$"; + + public static final String NAME = "SIGNOUT"; + + public SignoutRule() { + super(NAME, PATTERN); + } +} diff --git a/app/src/main/java/lightcontainer/repository/ClientHandlerRepository.java b/app/src/main/java/lightcontainer/repository/ClientHandlerRepository.java new file mode 100644 index 0000000..1f288ea --- /dev/null +++ b/app/src/main/java/lightcontainer/repository/ClientHandlerRepository.java @@ -0,0 +1,81 @@ +package lightcontainer.repository; + +import lightcontainer.domains.client.ClientHandler; +import lightcontainer.domains.server.UnicastServerListener; +import lightcontainer.interfaces.UnicastCHR; +import lightcontainer.protocol.ProtocolWriter; + +import java.util.ArrayList; +import java.util.List; +// TODO : C'est genre un ClientHandlerManager quoi hein, normal qu'il fasse blinder de chose ;) +/** + * ClientHandlerRepository + * + * Repository storing ClientHandler class. + * Contains some utility functions. + * + * @version 1.0 + * @since 1.0 + * + * @see ClientHandler + * @see AutoCloseable + * @author Jérémi NIHART + */ +public class ClientHandlerRepository implements AutoCloseable, UnicastCHR { + // Variable + private final List handlers; + private UnicastServerListener server; + + // Constructor + public ClientHandlerRepository() { + this.handlers = new ArrayList<>(); + } + + /** + * Setter, allow to define the ServerListener of a repository. + * & Start the server. + * @param server ServerListener to set as default. + * + * @since 1.0 + */ + @Override + public void setServerListener(UnicastServerListener server) { + this.server = server; + new Thread(server).start(); + } + + /** + * Add a ClientHandler. + * @param client Client Handler to add. + * + * @since 1.0 + */ + @Override + public void addClient(ClientHandler client) { + this.handlers.add(client); + } + + @Override + public void respondToClient(String login, ProtocolWriter.ProtocolResult response) { + for (ClientHandler client : handlers) { + if (client.getLogin().equals(login)) { + client.respond(response); + break; + } + } + } + + /** + * AutoClosable Function + * Closes all ClientHandlers stored in this repository and deallocates all resources. + * + * @since 1.0 + */ + @Override + public void close() { + // Stop the server. + this.server.stop(); + // Stop each clients. + this.handlers.forEach(ClientHandler::close); + } +} diff --git a/app/src/main/java/lightcontainer/repository/FileFrontEnd.java b/app/src/main/java/lightcontainer/repository/FileFrontEnd.java new file mode 100644 index 0000000..db6ec0c --- /dev/null +++ b/app/src/main/java/lightcontainer/repository/FileFrontEnd.java @@ -0,0 +1,84 @@ +package lightcontainer.repository; + +import lightcontainer.domains.client.StoreProcessor; +import lightcontainer.domains.Task; +import lightcontainer.enumerations.TaskStatus; +import lightcontainer.interfaces.ClientHandlerFFE; +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.interfaces.StoreProcessorFFE; +import lightcontainer.protocol.ProtocolWriter; + +import java.util.Deque; +import java.util.Iterator; +import java.util.concurrent.ConcurrentLinkedDeque; + +public class FileFrontEnd implements ClientHandlerFFE, StoreProcessorFFE { + // Variables + private Deque tasks = new ConcurrentLinkedDeque<>(); + private ClientHandlerRepository clientRepository; // TODO -> pourquoi pas une interface ? end + private StoreProcessorRepository storeRepository; // TODO -> pourquoi pas une interface ? end + private ProtocolRepository protocolRepository; + + // Constructor + public FileFrontEnd(ClientHandlerRepository clientRepo, StoreProcessorRepository storeRepo, ProtocolRepository protocolRepository) { + this.clientRepository = clientRepo; + this.storeRepository = storeRepo; + this.protocolRepository = protocolRepository; + } + + /** + * Appelé quand nouvelle tâche + */ + public void alertStoreProcessors(Task task) { + // On avertit les stor processors d'une nouvelle tâche + String stor = storeRepository.findDomain(task); + if (stor != null) { + storeRepository.assignTask(stor, task); + task.setDomain(stor); + } + } + + /** + * Permet à un {@link StoreProcessor} d'avertir le FFE qu'il est disponible + * + * @param store Le SBE qui s'est occupé de la tâche + * @param response La réponse à envoyer au client + */ + @Override + public void onStoreAvailable(StoreProcessor store, ProtocolWriter.ProtocolResult response) { + // TODO : Chercher une tâche appropriée + if (response != null) { + Iterator it = tasks.iterator(); + while (it.hasNext()) { + Task task = it.next(); + if (task.isResponseOfClient(store.getDomain())) { + clientRepository.respondToClient(task.getClient(), response); + it.remove(); // Suppression de la tâche + break; + } + } + } + + assignOtherTask(store); + } + + private void assignOtherTask(StoreProcessor store) { + Iterator it = tasks.iterator(); + + while (it.hasNext()) { + Task task = it.next(); + if (store.canProcessTask(task)) { + storeRepository.assignTask(store.getDomain(), task); + task.setDomain(store.getDomain()); + } + } + } + + + @Override + public void newCommand(ProtocolWriter.ProtocolResult command, String client) { + Task task = Task.newInstance(command, client); + tasks.add(task); + alertStoreProcessors(task); + } +} diff --git a/app/src/main/java/lightcontainer/repository/ProtocolRepositoryImpl.java b/app/src/main/java/lightcontainer/repository/ProtocolRepositoryImpl.java new file mode 100644 index 0000000..86da726 --- /dev/null +++ b/app/src/main/java/lightcontainer/repository/ProtocolRepositoryImpl.java @@ -0,0 +1,45 @@ +package lightcontainer.repository; + +import lightcontainer.interfaces.ProtocolRepository; +import lightcontainer.protocol.ProtocolReader; +import lightcontainer.protocol.ProtocolWriter; + +import java.util.HashSet; +import java.util.Set; + +public class ProtocolRepositoryImpl implements ProtocolRepository { + private final Set readers = new HashSet<>(); + private final Set writers = new HashSet<>(); + + @Override + public T executeReader(String data) { + for (ProtocolReader reader : readers) { + T readerResult = reader.execute(data); + if (readerResult != null) { + return readerResult; + } + } + return null; + } + + @Override + public T executeWriter(String cmdName, String... data) { + for (ProtocolWriter writer : writers) { + T command; + if (cmdName.equals(writer.getCmdName()) && (command = writer.execute(data)) != null) { + return command; + } + } + return null; + } + + @Override + public void addReader(ProtocolReader reader) { + this.readers.add(reader); + } + + @Override + public void addWriter(ProtocolWriter writer) { + this.writers.add(writer); + } +} diff --git a/app/src/main/java/lightcontainer/repository/StoreProcessorRepository.java b/app/src/main/java/lightcontainer/repository/StoreProcessorRepository.java new file mode 100644 index 0000000..fd6bd4d --- /dev/null +++ b/app/src/main/java/lightcontainer/repository/StoreProcessorRepository.java @@ -0,0 +1,93 @@ +package lightcontainer.repository; + +import lightcontainer.domains.Task; +import lightcontainer.domains.client.StoreProcessor; +import lightcontainer.domains.server.MulticastServerListener; +import lightcontainer.interfaces.MulticastSPR; + +import java.util.HashSet; +import java.util.Set; +// TODO : C'est genre un ClientHandlerManager quoi hein, normal qu'il fasse blinder de chose ;) +/** + * StoreProcessorRepository + * + * Repository storing StorePorcessor class. + * Contains some utility functions. + * + * @version 1.0 + * @since 1.0 + * + * @see StoreProcessor + * @author Jérémi NIHART + */ +public class StoreProcessorRepository implements AutoCloseable, MulticastSPR { + // Variables + private final Set handlers; + private MulticastServerListener server; + + // Constructor + public StoreProcessorRepository() { + this.handlers = new HashSet<>(); + } + + /** + * Setter, allow to define the ServerListener of a repository. + * & start the server. + * @param server ServerListener to set as default. + * + * @since 1.0 + */ + @Override + public void setServerListener(MulticastServerListener server) { + this.server = server; + new Thread(server).start(); + } + + /** + * Add a StorePorcessor. + * @param store Store processor to add. + * + * @since 1.0 + */ + @Override + public void addStore(StoreProcessor store) { + this.handlers.add(store); + + } + + @Override + public String findDomain(Task task) { + StoreProcessor handler = findSBE(task); + return handler == null ? null : handler.getDomain(); + } + + private StoreProcessor findSBE(Task task) { + for (StoreProcessor handler : handlers) { + if (handler.canProcessTask(task)) { + return handler; + } + } + + return null; + } + + @Override + public void assignTask(String stor, Task task) { + StoreProcessor handler = findSBE(task); + handler.executeCommand(task.getCommand()); + } + + /** + * AutoClosable Function + * Closes all StoreProcessor stored in this repository and deallocates all resources. + * + * @since 1.0 + */ + @Override + public void close() { + // Stop the server. + this.server.stop(); + // Close each client. + this.handlers.forEach(StoreProcessor::close); + } +} diff --git a/app/src/main/java/lightcontainer/storage/Adapter.java b/app/src/main/java/lightcontainer/storage/Adapter.java new file mode 100644 index 0000000..29d5dc3 --- /dev/null +++ b/app/src/main/java/lightcontainer/storage/Adapter.java @@ -0,0 +1,8 @@ +package lightcontainer.storage; + +public interface Adapter { + + String toString(); + + AppData fromString(String appDataString); +} diff --git a/app/src/main/java/lightcontainer/storage/AppConfig.java b/app/src/main/java/lightcontainer/storage/AppConfig.java new file mode 100644 index 0000000..7f767b8 --- /dev/null +++ b/app/src/main/java/lightcontainer/storage/AppConfig.java @@ -0,0 +1,88 @@ +package lightcontainer.storage; + +/** + * AppConfig represents all network related information needed for the program to work. + * + * @author Maximilien LEDOUX + * @version 1.0 + * @since 1.0 + */ +public class AppConfig { + + private static AppConfig instance = null; + private int unicastPort; + private String multicastIp; + private int multicastPort; + private String networkInterface; + private boolean isTls; + + /** + * Constructs a new instance of AppConfig. + * Sets all data to default values. + */ + private AppConfig() { + this.unicastPort = -1; + this.multicastIp = "NONE"; + this.multicastPort = -1; + this.networkInterface = "NONE"; + this.isTls = false; + } + + /** + * @return An instance of this class. Always returns the same instance. + */ + public static AppConfig getInstance() { + if (instance == null) { + instance = new AppConfig(); + } + return instance; + } + + public int getUnicastPort() { + return unicastPort; + } + + public void setUnicastPort(int unicastPort) { + if (this.unicastPort == -1) { + this.unicastPort = unicastPort; + } + } + + public String getMulticastIp() { + return multicastIp; + } + + public void setMulticastIp(String multicastIp) { + if (this.multicastIp.equals("NONE")) { + this.multicastIp = multicastIp; + } + } + + public int getMulticastPort() { + return multicastPort; + } + + public void setMulticastPort(int multicastPort) { + if (this.multicastPort == -1) { + this.multicastPort = multicastPort; + } + } + + public String getNetworkInterface() { + return networkInterface; + } + + public void setNetworkInterface(String networkInterface) { + if (this.networkInterface.equals("NONE")) { + this.networkInterface = networkInterface; + } + } + + public boolean isTls() { + return isTls; + } + + public void setTls(boolean tls) { + this.isTls = tls; + } +} diff --git a/app/src/main/java/lightcontainer/storage/AppData.java b/app/src/main/java/lightcontainer/storage/AppData.java new file mode 100644 index 0000000..2a693b2 --- /dev/null +++ b/app/src/main/java/lightcontainer/storage/AppData.java @@ -0,0 +1,152 @@ +package lightcontainer.storage; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * AppData represents the database of the FileFrontEnd program. + * It contains an AppConfig instance and a collection of Users. + * + * @author Maximilien LEDOUX + * @version 1.0 + * @see AppConfig + * @see User + * @since 1.0 + */ +public class AppData { + + private static AppData instance = null; + private AppConfig appConfig; + private final Map users; + + + /** + * Constructs a new instance of AppData. + * Sets appConfig to null and creates a new Hashmap of users. + */ + private AppData() { + this.appConfig = null; + this.users = new HashMap<>(); + } + + /** + * @return An instance of this class. Always returns the same instance. + */ + public static AppData getInstance() { + if (instance == null) { + instance = new AppData(); + } + return instance; + } + + /** + * @return The AppConfig + */ + public AppConfig getAppConfig() { + return appConfig; + } + + /** + * Sets the AppConfig. This method sets the AppConfig for once and for all. + * It is locked after first call. + * + * @param appConfig The network configuration of the program. + */ + public void setAppConfig(AppConfig appConfig) { + if (this.appConfig == null) { + this.appConfig = appConfig; + } + } + + /** + * @param userName The name of the user. + * @return The user corresponding to userName, null otherwise. + */ + public User getUser(String userName) { + return this.users.get(userName); + } + + /** + * Use this method when a user signs up. + * + * @param user The user to add. + * @return True if the user was added. False if a user with the same name already exists. + */ + public boolean addUser(User user) { + if (this.users.containsKey(user.getName())) { + return false; + } else { + this.users.put(user.getName(), user); + return true; + } + } + + public Iterator usersIterator() { + return users.values().iterator(); + } + + /** + * @param fileName The name of the file + * @param user The user + * @return The file corresponding to the given name and belonging to the user. Null if the user cannot be found or the file cannot be found + * @deprecated Maybe not useful. DO NOT USE FOR THE TIME BEING + */ + public File getFileOf(String fileName, User user) { + return this.users.get(user.getName()).getFile(fileName); + } + + /** + * Call this method after receiving SAVEFILE_OK from the StorBackEnd. + * Do NOT call when receiving SAVEFILE_ERROR, or it will break the system's synchronization. + *

+ * Adds the file of for a specific user. + * True indicates the success of the operation. + * False indicates the failure of the operation. + * + * @param file The file to add + * @param user The user who wants to add the file + * @return True if the user is found and a file with the same name doesn't already exist for this user. False otherwise. + */ + public boolean addFileFor(File file, User user) { + if (!this.users.containsKey(user.getName())) { + return false; + } else { + this.users.get(user.getName()).addFile(file); + return true; + } + } + + /** + * Call this method after receiving REMOVEFILE_OK from the StorBackEnd. + * Do NOT call when receiving REMOVEFILE_ERROR, or it will break the system's synchronization. + * Deletes the file of for a specific user. + * True indicates the success of the operation. + * False indicates the failure of the operation. + * + * @param fileName The name of the file to delete + * @param user The user who wants to delete the file + * @return True if the user is found and the file was deleted. False otherwise. + */ + public boolean deleteFileOf(String fileName, User user) { + if (!this.users.containsKey(user.getName())) { + return false; + } else { + return this.users.get(user.getName()).deleteFile(fileName); + } + } + + /** + * @param user The user who wants to add a storage for their file + * @param file The file that needs a new storage + * @param storage The storage to add + * @return True if the storage was added. False otherwise. + */ + public boolean addStorage(User user, File file, String storage) { + if (!this.users.containsKey(user.getName())) { + return false; + } else { + return this.users.get(user.getName()).addStorage(file, storage); + } + } +} diff --git a/app/src/main/java/lightcontainer/storage/File.java b/app/src/main/java/lightcontainer/storage/File.java new file mode 100644 index 0000000..e7d5d5f --- /dev/null +++ b/app/src/main/java/lightcontainer/storage/File.java @@ -0,0 +1,47 @@ +package lightcontainer.storage; + +import java.util.Iterator; +import java.util.Set; + +/** + * File represents all information related to a file + */ +public class File { + + private final String name; + private final int size; + private final String iv; + private final Set storage; + + public File(String name, int size, String iv, Set storage) { + this.name = name; + this.size = size; + this.iv = iv; + this.storage = storage; + } + + public String getName() { + return name; + } + + public int getSize() { + return size; + } + + public String getIv() { + return iv; + } + + public Iterator getStorageIterator() { + return storage.iterator(); + } + + public boolean addStorage(String storage) { + if (this.storage.contains(storage)) { + return false; + } else { + this.storage.add(storage); + return true; + } + } +} diff --git a/app/src/main/java/lightcontainer/storage/JsonAdapter.java b/app/src/main/java/lightcontainer/storage/JsonAdapter.java new file mode 100644 index 0000000..aaecc55 --- /dev/null +++ b/app/src/main/java/lightcontainer/storage/JsonAdapter.java @@ -0,0 +1,145 @@ +package lightcontainer.storage; + +import com.google.gson.*; + +import java.util.*; + +/** + * Specific implementation of Adapter that converts AppData to Json and vice-versa + */ +public class JsonAdapter implements Adapter { + + private AppData appData; + + public JsonAdapter(AppData appData) { + this.appData = appData; + } + + /** + * + * @return A Json String containing AppData properties + */ + @Override + public String toString() { + return addData(appData); + } + + private String addData(AppData appData) { + AppConfig appConfig = appData.getAppConfig(); + JsonObject config = new JsonObject(); + config.addProperty("unicast_port", appConfig.getUnicastPort()); + config.addProperty("multicast_ip", appConfig.getMulticastIp()); + config.addProperty("multicast_port", appConfig.getMulticastPort()); + config.addProperty("network_interface", appConfig.getNetworkInterface()); + config.addProperty("tls", appConfig.isTls()); + JsonArray users = new JsonArray(); + Iterator userIterator = appData.usersIterator(); + addUsers(users, userIterator); + config.add("users", users); + return config.toString(); + } + + private void addUsers(JsonArray users, Iterator userIterator) { + while (userIterator.hasNext()) { + User current = userIterator.next(); + JsonObject user = new JsonObject(); + user.addProperty("name", current.getName()); + user.addProperty("password", current.getPassword()); + user.addProperty("aes_key", current.getAesKey()); + JsonArray files = new JsonArray(); + Iterator fileIterator = current.fileIterator(); + addFiles(fileIterator, files); + user.add("files", files); + users.add(user); + } + } + + private void addFiles(Iterator fileIterator, JsonArray files) { + while (fileIterator.hasNext()) { + File currentFile = fileIterator.next(); + JsonObject file = new JsonObject(); + file.addProperty("name", currentFile.getName()); + file.addProperty("size", currentFile.getSize()); + file.addProperty("iv", currentFile.getIv()); + JsonArray storage = new JsonArray(); + Iterator storageIterator = currentFile.getStorageIterator(); + addStorage(storage, storageIterator); + file.add("storage", storage); + files.add(file); + } + } + + private void addStorage(JsonArray storage, Iterator storageIterator) { + while (storageIterator.hasNext()) { + String storageString = storageIterator.next(); + storage.add(storageString); + } + } + + /** + * + * @param appDataString The Json String to convert + * @return An AppData instance + */ + @Override + public AppData fromString(String appDataString) { + try { + JsonElement jsonString = JsonParser.parseString(appDataString); + JsonObject jsonAppData = jsonString.getAsJsonObject(); + AppConfig appConfig = AppConfig.getInstance(); + appConfig.setUnicastPort(jsonAppData.get("unicast_port").getAsInt()); + appConfig.setMulticastIp(jsonAppData.get("multicast_ip").getAsString()); + appConfig.setMulticastPort(jsonAppData.get("multicast_port").getAsInt()); + appConfig.setNetworkInterface(jsonAppData.get("network_interface").getAsString()); + appConfig.setTls(jsonAppData.get("tls").getAsBoolean()); + JsonArray jsonUsers = jsonAppData.getAsJsonArray("users"); + List users = new ArrayList<>(); + getUsers(jsonUsers, users); + AppData appData = AppData.getInstance(); + appData.setAppConfig(appConfig); + for (User user : users) { + appData.addUser(user); + } + this.appData = appData; + return this.appData; + } catch (JsonParseException parseException) { + System.out.println("[FFE] : Error while loading configuration file"); //TODO - changer en log + return null; + } + } + + private void getUsers(JsonArray jsonUsers, List users) { + for (JsonElement element : jsonUsers) { + JsonObject jsonUser = element.getAsJsonObject(); + String name = jsonUser.get("name").getAsString(); + String password = jsonUser.get("password").getAsString(); + String aeskey = jsonUser.get("aes_key").getAsString(); + Map userFiles = new HashMap<>(); + JsonArray jsonFiles = jsonUser.getAsJsonArray("files"); + getFiles(userFiles, jsonFiles); + User user = new User(name, password, aeskey, userFiles); + users.add(user); + } + } + + private void getFiles(Map userFiles, JsonArray jsonFiles) { + for (JsonElement fileElement : jsonFiles) { + JsonObject jsonFile = fileElement.getAsJsonObject(); + String fileName = jsonFile.get("name").getAsString(); + int size = jsonFile.get("size").getAsInt(); + String iv = jsonFile.get("iv").getAsString(); + Set storage = new HashSet<>(); + JsonArray jsonStorage = jsonFile.getAsJsonArray("storage"); + getStorage(storage, jsonStorage); + File file = new File(fileName, size, iv, storage); + userFiles.put(file.getName(), file); + } + } + + private void getStorage(Set storage, JsonArray jsonStorage) { + for (JsonElement storageElement : jsonStorage) { + String storageName = storageElement.getAsString(); + storage.add(storageName); + } + } +} diff --git a/app/src/main/java/lightcontainer/storage/Repository.java b/app/src/main/java/lightcontainer/storage/Repository.java new file mode 100644 index 0000000..1f085a6 --- /dev/null +++ b/app/src/main/java/lightcontainer/storage/Repository.java @@ -0,0 +1,51 @@ +package lightcontainer.storage; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +public class Repository { + + /** + * @param filePath The path where the file must be saved + * @param adapter The service that converts Objects to Strings + */ + static void save(String filePath, Adapter adapter) { + if (filePath != null) { + String jsonAppData = adapter.toString(); + try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get(filePath).toAbsolutePath(), StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { + bufferedWriter.write(jsonAppData); + bufferedWriter.flush(); + } catch (IOException e) { + System.out.println("Error while saving configuration file !"); + } + } + } + + /** + * @param filePath The path where the file is stored + * @param adapter The service that converts Strings to objects + * @return + */ + static AppData load(String filePath, Adapter adapter) { + String jsonString = readFile(filePath); + return adapter.fromString(jsonString); + } + + private static String readFile(String filePath) { + StringBuilder builder = new StringBuilder(); + try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath).toAbsolutePath(), StandardCharsets.UTF_8)) { + while (reader.ready()) { + builder.append(reader.readLine()); + } + } catch (IOException e) { + System.out.println("Error while reading configuration file"); + builder.setLength(0); + } + return builder.toString(); + } +} diff --git a/app/src/main/java/lightcontainer/storage/User.java b/app/src/main/java/lightcontainer/storage/User.java new file mode 100644 index 0000000..47a65f9 --- /dev/null +++ b/app/src/main/java/lightcontainer/storage/User.java @@ -0,0 +1,80 @@ +package lightcontainer.storage; + +import java.util.Iterator; +import java.util.Map; + +/** + * User represents a user of the system. + * + * @author Maximilien LEDOUX + * @version 1.0 + * @since 1.0 + */ +public class User { + + private final String Name; + private final String password; + private final String aesKey; + private final Map files; + + public User(String Name, String password, String aesKey, Map files) { + this.Name = Name; + this.password = password; + this.aesKey = aesKey; + this.files = files; + } + + public String getName() { + return Name; + } + + public String getPassword() { + return password; + } + + public String getAesKey() { + return aesKey; + } + + public Iterator fileIterator() { + return files.values().iterator(); + } + + public File getFile(String fileName) { + return this.files.get(fileName); + } + + /** + * @param file The file to add. + * @return False if a file with the same name already exists. Otherwise, adds the file and returns true. + */ + public void addFile(File file) { + this.files.put(file.getName(), file); + } + + /** + * @param fileName The name of the file to delete. + * @return True if the file was deleted. False otherwise. + */ + public boolean deleteFile(String fileName) { + if (this.files.containsKey(fileName)) { + this.files.remove(fileName); + return true; + } else { + return false; + } + } + + /** + * @param file The file that needs a storage + * @param storage The storage name + * @return True if the storage was added to the file. False otherwise. + */ + public boolean addStorage(File file, String storage) { + if (this.files.containsKey(file.getName())) { + return file.addStorage(storage); + } else { + return false; + } + } +} diff --git a/app/src/main/java/lightcontainer/storage/json-example.json b/app/src/main/java/lightcontainer/storage/json-example.json new file mode 100644 index 0000000..6f982b6 --- /dev/null +++ b/app/src/main/java/lightcontainer/storage/json-example.json @@ -0,0 +1,38 @@ +{ + "unicast_port": 32000, + "multicast_ip": "224.25.0.1", + "multicast_port": 70000, + "network_interface": "LALALALA", + "tls": false, + "users": [ + { + "username": "endmove", + "password": "notre-hash", + "aes_key": "jkjiezjijfizef8e4864", + "files": [ + { + "file_name": "lol.jpeg", + "file_size": 15558, + "iv": "8d484e8e84 (lié à la méthode de cryptage)", + "storage": [ + "lol.benjamin" + ] + }, + { + "file_name": "super-man.png", + "file_size": 24457, + "iv": "zf4fe84f (lié à la méthode de cryptage)", + "storage": [ + "lol.benjamin" + ] + } + ] + }, + { + "username": "michel", + "password": "notre-hash du mdp", + "aes_key": "fjshufihehehuhuhuehu", + "files": [] + } + ] +} \ No newline at end of file diff --git a/app/src/main/java/lightcontainer/utils/AES_GCM.java b/app/src/main/java/lightcontainer/utils/AES_GCM.java new file mode 100644 index 0000000..5aeed9d --- /dev/null +++ b/app/src/main/java/lightcontainer/utils/AES_GCM.java @@ -0,0 +1,126 @@ +package lightcontainer.utils; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Base64; + +public class AES_GCM { + // Constants + public static final int AES_KEY_SIZE = 256; + public static final int GCM_IV_LENGTH = 16; + public static final int GCM_TAG_LENGTH = 16; + + public static void main(String[] args) throws Exception + { + // Text pour test : + String plainText = "salut fils de pute"; + + String IV = generateIV(); + String key = generateSecretKey(); + + System.out.println("Original Text : " + plainText); + + byte[] cipherText = encrypt(plainText.getBytes(), key, IV); + System.out.println("Encrypted Text : " + Base64.getEncoder().encodeToString(cipherText)); + + String decryptedText = decrypt(cipherText, key, IV); + System.out.println("DeCrypted Text : " + decryptedText); + } + + /** + * Decoder to decode base64 vector to byte vector. + * @param base64Vector A base64 encoded vector. + * @return Byte vector. + */ + private static byte[] decodeBase64(String base64Vector) { + Base64.Decoder b64Decoder = Base64.getDecoder(); + return b64Decoder.decode(base64Vector); + } + + /** + * Encoder to encode vector to base64 string. + * @param rawVector A raw vector. + * @return A base64 encoded vector. + */ + private static String encodeBase64(byte[] rawVector) { + Base64.Encoder b64Encoder = Base64.getEncoder(); + return b64Encoder.encodeToString(rawVector); + } + + /** + * Generate a secret key base64 encoded. + * @return New Secret key b64 encoded. + */ + public static String generateSecretKey() throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(AES_KEY_SIZE); + SecretKey key = keyGenerator.generateKey(); + return encodeBase64(key.getEncoded()); + } + + /** + * Generate an IV (initialisation vector) base64 encoded. + * @return New generated IV b64 encoded. + */ + public static String generateIV() { + byte[] IV = new byte[GCM_IV_LENGTH]; + SecureRandom random = new SecureRandom(); + random.nextBytes(IV); + return encodeBase64(IV); + } + + /** + * Encrypt, with AES GCM. + * @param plainContent Content to encrypt. + * @param key Base64 encoded secret key. + * @param IV Base64 encoded vector. + * @return The encrypted cipherContent. + */ + public static byte[] encrypt(byte[] plainContent, String key, String IV) throws Exception + { + // Get Cipher Instance + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + // Create SecretKeySpec + SecretKeySpec keySpec = new SecretKeySpec(decodeBase64(key), "AES"); + + // Create GCMParameterSpec + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, decodeBase64(IV)); + + // Initialize Cipher for ENCRYPT_MODE + cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec); + + // Perform Encryption + return cipher.doFinal(plainContent); + } + + /** + * Decrypt, with AES GCM. + * @param cipherContent The encrypted cipherContent + * @param key Base64 encoded secret key. + * @param IV Base64 encoded vector. + * @return The decrypted plainContent. + */ + public static String decrypt(byte[] cipherContent, String key, String IV) throws Exception + { + // Get Cipher Instance + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + + // Create SecretKeySpec + SecretKeySpec keySpec = new SecretKeySpec(decodeBase64(key), "AES"); + + // Create GCMParameterSpec + GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH*8, decodeBase64(IV)); + + // Initialize Cipher for DECRYPT_MODE + cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); + + // Perform Decryption + return new String(cipher.doFinal(cipherContent)); + } +} \ No newline at end of file diff --git a/app/src/main/java/lightcontainer/utils/FileReceiver.java b/app/src/main/java/lightcontainer/utils/FileReceiver.java new file mode 100644 index 0000000..c59452f --- /dev/null +++ b/app/src/main/java/lightcontainer/utils/FileReceiver.java @@ -0,0 +1,37 @@ +package lightcontainer.utils; + +import java.io.BufferedOutputStream; +import java.io.FileOutputStream; +import java.io.InputStream; + +public class FileReceiver { + private static final int DEFAULT_BUFFER = 8000; + private String path; + + public FileReceiver(String path) { this.path = path; } + + public boolean receiveFile(InputStream input, String fileName, long fileSize) { + int bytesReceived = 0; + BufferedOutputStream bosFile = null; + + try { + byte[] buffer = new byte[DEFAULT_BUFFER]; + bosFile = new BufferedOutputStream(new FileOutputStream(String.format("%s/%s", path, fileName))); + long currentOffset = 0; + + while((currentOffset < fileSize) && ((bytesReceived = input.read(buffer)) > 0)) { + bosFile.write(buffer, 0, bytesReceived); + currentOffset += bytesReceived; + } + bosFile.flush(); + bosFile.close(); + + return true; + } catch(Exception ex) { + ex.printStackTrace(); + if(bosFile != null) { try { bosFile.close(); } catch(Exception e) {} } + return false; + } + } + +} diff --git a/app/src/main/java/lightcontainer/utils/FileSender.java b/app/src/main/java/lightcontainer/utils/FileSender.java new file mode 100644 index 0000000..92f93f3 --- /dev/null +++ b/app/src/main/java/lightcontainer/utils/FileSender.java @@ -0,0 +1,35 @@ +package lightcontainer.utils; + +import java.io.*; + +public class FileSender { + private static final int DEFAULT_BUFFER=8000; + private String path; + + public FileSender(String path) { this.path = path; } + + public boolean sendFile(String filename, OutputStream out) { + BufferedInputStream bisFile = null; + int bytesReaded = 0; + + try { + File f = new File(String.format("%s/%s", path, filename)); + long fileSize = f.length(); + if(f.exists()) { + byte[] buffer = new byte[DEFAULT_BUFFER]; + bisFile = new BufferedInputStream(new FileInputStream(f)); + long currentOffset = 0; + while((currentOffset < fileSize) && (bytesReaded = bisFile.read(buffer)) > 0) { + out.write(buffer, 0, bytesReaded); out.flush(); + currentOffset+= bytesReaded; + } + bisFile.close(); + return true; + } else + return false; + } catch(IOException ex) { + ex.printStackTrace(); + return false; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/lightcontainer/utils/NetChooser.java b/app/src/main/java/lightcontainer/utils/NetChooser.java new file mode 100644 index 0000000..f9ecd98 --- /dev/null +++ b/app/src/main/java/lightcontainer/utils/NetChooser.java @@ -0,0 +1,69 @@ +package lightcontainer.utils; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Scanner; + +public class NetChooser { + private List interfaces; + + public NetChooser() { + loadInterfaces(); + Scanner console = new Scanner(System.in); + String[] allInterfaceNames = getInterfaces(); + for(int index=0; index < allInterfaceNames.length; ++index) { + System.out.printf("%d. %s\n", index, allInterfaceNames[index]); + } + System.out.printf("Select your interface :"); + NetworkInterface selected = getInterfacesByIndex(console.nextInt()); + System.out.printf("Selected interface: %s\n", selected.getDisplayName()); + + } + + private void loadInterfaces() { + try { + interfaces = new ArrayList<>(); + Enumeration discoveredInterfaces = NetworkInterface.getNetworkInterfaces(); + while (discoveredInterfaces.hasMoreElements()) { + NetworkInterface currentInterface = discoveredInterfaces.nextElement(); + Enumeration inetAddresses = currentInterface.getInetAddresses(); + int ipCount = 0; + while(inetAddresses.hasMoreElements()) { + InetAddress currentAddress = inetAddresses.nextElement(); + ipCount++; + } + if(ipCount > 0) + interfaces.add(currentInterface); + } + } catch(SocketException ex) { + ex.printStackTrace(); + } + + } + + public NetworkInterface getInterfacesByIndex(int i) { + if(i >= 0) + return interfaces.get(i); + else + return null; + } + + public String[] getInterfaces() { + if(interfaces.size() > 0) { + String[] result = new String[interfaces.size()]; + for (int i = 0; i < interfaces.size(); ++i) { + result[i] = interfaces.get(i).getDisplayName(); + } + return result; + } else + return null; + } + + public static void main(String[] args) { + new NetChooser(); + } +} \ No newline at end of file diff --git a/app/src/main/resources/rules.txt b/app/src/main/resources/rules.txt new file mode 100644 index 0000000..8be9487 --- /dev/null +++ b/app/src/main/resources/rules.txt @@ -0,0 +1,43 @@ +//Standardized definitions +digit = [0-9] +port = (6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}) +size = [0-9]{1,10} +line = \r\n +visiblechar = \p{Print} +passchar = [^ !] +binary = . +password = [^ !]{5,50} +bl = //espace +letter = [A-Za-z] +digit_letter = [A-Za-z0-9] +filename = [^ !]{1,20} +domain = [A-Za-z0-9.]{5,20} +hash_filename = [A-Za-z0-9.]{50,200} +hash_filecontent = [A-Za-z0-9.]{50,200} +file_info = [A-Za-z0-9.]{50,200} [0-9]{1,10} [A-Za-z0-9.]{50,200} +login = [A-Za-z0-9]{2,20} + +//StorBackEnd to FileFrontEnd +sbe_hello = ^HELLO ([A-Za-z0-9.]{5,20}) ([\d]{0,5})\r\n$ //TODO \r\n -> à tester pour voir si déjà dans le flux ou doit être construit + +//FileFrontEnd to StorBackEnd +ffe_sendfile = ^SENDFILE ([A-Za-z0-9.]{50,200} [0-9]{1,10} [A-Za-z0-9.]{50,200})\r\n$ +sbe_sendresult = ^(SEND_OK|SEND_ERROR)\r\n$ +ffe_erasefile = ^ERASEFILE ([A-Za-z0-9.]{50,200})\r\n$ +sbe_eraseresult = ^(ERASE_OK|ERASE_ERROR)\r\n$ +ffe_retrievefile = ^RETRIEVEFILE ([A-Za-z0-9.]{50,200})\r\n$ +sbe_retrieveresult = ^(RETRIEVE_OK ([A-Za-z0-9.]{50,200} [0-9]{1,10} [A-Za-z0-9.]{50,200})\r\n)|(RETRIEVE_ERROR\r\n)$ + +//Client to FileFrontEnd +client_signin = ^SIGNIN ([A-Za-z0-9]{2,20}) ([^ !]{5,50})\r\n$ +client_signup = ^SIGNUP ([A-Za-z0-9]{2,20}) ([^ !]{5,50})\r\n$ +ffe_signresult = ^(SIGN_OK|SIGN_ERROR)\r\n$ +client_filelist = ^FILELIST\r\n$ +ffe_filelistresult = ^FILES( ([^ !]{1,20})!([0-9]{1,10})){0,50}$ +client_savefile = ^SAVE_FILE ([^ !]{1,20}) ([0-9]{1,10})\r\n$ +ffe_savefileresult = ^(SAVEFILE_OK|SAVEFILE_ERROR)\r\n$ +client_getfile = ^GETFILE ([^ !]{1,20})\r\n$ +ffe_getfileresult = ^(GETFILE_OK (^ !]{1,20}) ([0-9]{1,10})\r\n)|(GETFILE_ERROR\r\n)$ +client_removefile = ^REMOVEFILE ([^ !]{1,20})\r\n$ +ffe_removefileresult = ^(REMOVEFILE_OK|REMOVEFILE_ERROR)\r\n$ +client_signout = ^SIGNOUT\r\n$ \ No newline at end of file diff --git a/app/src/test/java/lightcontainer/AppTest.java b/app/src/test/java/lightcontainer/AppTest.java new file mode 100644 index 0000000..9d64c1d --- /dev/null +++ b/app/src/test/java/lightcontainer/AppTest.java @@ -0,0 +1,14 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package lightcontainer; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class AppTest { +// @Test void appHasAGreeting() { +// App classUnderTest = new App(); +// assertNotNull(classUnderTest.getGreeting(), "app should have a greeting"); +// } +} diff --git a/app/src/test/java/lightcontainer/protocol/rules/reader/HelloRuleTest.java b/app/src/test/java/lightcontainer/protocol/rules/reader/HelloRuleTest.java new file mode 100644 index 0000000..a356fb5 --- /dev/null +++ b/app/src/test/java/lightcontainer/protocol/rules/reader/HelloRuleTest.java @@ -0,0 +1,24 @@ +package lightcontainer.protocol.rules.reader; + +import lightcontainer.protocol.ProtocolReader; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class HelloRuleTest { + + @Test + public void whenRuleIsRightThenIsExecute() { + // GIVEN + ProtocolReader protocolReader = new HelloRule(); + String request = "HELLO bento 42890\r\n"; + + // WHEN + HelloRule.Result ruleResult = protocolReader.execute(request); + + // THEN + assertEquals("bento", ruleResult.getDomain()); + assertEquals(42890, ruleResult.getPort()); + } + +} \ No newline at end of file diff --git a/app/src/test/java/lightcontainer/protocol/rules/writer/SignoutRuleTest.java b/app/src/test/java/lightcontainer/protocol/rules/writer/SignoutRuleTest.java new file mode 100644 index 0000000..31bf799 --- /dev/null +++ b/app/src/test/java/lightcontainer/protocol/rules/writer/SignoutRuleTest.java @@ -0,0 +1,21 @@ +package lightcontainer.protocol.rules.writer; + +import lightcontainer.protocol.ProtocolWriter; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class SignoutRuleTest { + + @Test + public void whenRuleIsRightThenReturnCommand() { + //GIVEN + ProtocolWriter protocolWriter = new SignoutRule(); + String[] datas = {}; + + //EXPECT + assertNotNull(protocolWriter.execute(datas)); + assertEquals("SIGNOUT\r\n", protocolWriter.execute(datas)); + } + +} \ No newline at end of file diff --git a/app/src/test/java/lightcontainer/storage/JsonAdapterTests.java b/app/src/test/java/lightcontainer/storage/JsonAdapterTests.java new file mode 100644 index 0000000..bd9006a --- /dev/null +++ b/app/src/test/java/lightcontainer/storage/JsonAdapterTests.java @@ -0,0 +1,66 @@ +package lightcontainer.storage; + +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + + +public class JsonAdapterTests { + + @Test + public void convertAppDataToJson() { + //GIVEN an AppData instance and a Json Adapter + AppData appData = AppData.getInstance(); + AppConfig appConfig = AppConfig.getInstance(); + appConfig.setUnicastPort(32000); + appConfig.setMulticastIp("224.25.0.1"); + appConfig.setMulticastPort(15502); + appConfig.setNetworkInterface("My network interface"); + appConfig.setTls(false); + Map files = new HashMap<>(); + Set storage = new HashSet<>(); + storage.add("StorBackEnd1"); + File file1 = new File("File1", 15, "8d8d8d8d", storage); + files.put(file1.getName(), file1); + User user1 = new User("User1", "Password", "djdjjdj", files); + appData.setAppConfig(appConfig); + appData.addUser(user1); + JsonAdapter jsonAdapter = new JsonAdapter(appData); + //WHEN the adapter converts AppData to Json + String jsonAppData = jsonAdapter.toString(); + //THEN + assertTrue(jsonAppData.contains("32000")); + assertTrue(jsonAppData.contains("224.25.0.1")); + assertTrue(jsonAppData.contains("15502")); + assertTrue(jsonAppData.contains("My network interface")); + assertTrue(jsonAppData.contains("false")); + assertTrue(jsonAppData.contains("User1")); + assertTrue(jsonAppData.contains("Password")); + assertTrue(jsonAppData.contains("djdjjdj")); + assertTrue(jsonAppData.contains("File1")); + assertTrue(jsonAppData.contains("15")); + assertTrue(jsonAppData.contains("8d8d8d8d")); + assertTrue(jsonAppData.contains("StorBackEnd1")); + } + + @Test + public void convertJsonToAppData() { + //GIVEN a Json string + String json = "{\"unicast_port\":32000,\"multicast_ip\":\"224.25.0.1\",\"multicast_port\":15502,\"network_interface\":\"My network interface\",\"tls\":false,\"users\":[{\"name\":\"User1\",\"password\":\"Password\",\"aes_key\":\"djdjjdj\",\"files\":[{\"name\":\"File1\",\"size\":15,\"iv\":\"8d8d8d8d\",\"storage\":[\"StorBackEnd1\"]}]}]}"; + //WHEN the adapter converts Json to Appdata + JsonAdapter jsonAdapter = new JsonAdapter(null); + AppData appData = jsonAdapter.fromString(json); + //THEN + assertNotNull(appData.getAppConfig()); + assertEquals("My network interface", appData.getAppConfig().getNetworkInterface()); + assertEquals(32000, appData.getAppConfig().getUnicastPort()); + assertEquals("224.25.0.1", appData.getAppConfig().getMulticastIp()); + assertEquals(15502, appData.getAppConfig().getMulticastPort()); + assertFalse(appData.getAppConfig().isTls()); + } +} diff --git a/app/src/test/java/lightcontainer/storage/RepositoryTests.java b/app/src/test/java/lightcontainer/storage/RepositoryTests.java new file mode 100644 index 0000000..ed90a68 --- /dev/null +++ b/app/src/test/java/lightcontainer/storage/RepositoryTests.java @@ -0,0 +1,71 @@ +package lightcontainer.storage; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + + +public class RepositoryTests { + + @AfterEach + public void destroyTestFile() { + try { + Files.deleteIfExists(Paths.get("src", "test", "resources", "test.json").toAbsolutePath()); + } catch (IOException e) { + System.out.println("Error while destroying file"); + } + } + + @Test + public void save() { + //GIVEN an AppData instance and a Json Adapter + AppData appData = AppData.getInstance(); + AppConfig appConfig = AppConfig.getInstance(); + appConfig.setUnicastPort(32000); + appConfig.setMulticastIp("224.25.0.1"); + appConfig.setMulticastPort(15502); + appConfig.setNetworkInterface("My network interface"); + appConfig.setTls(false); + Map files = new HashMap<>(); + Set storage = new HashSet<>(); + storage.add("StorBackEnd1"); + File file1 = new File("File1", 15, "8d8d8d8d", storage); + files.put(file1.getName(), file1); + User user1 = new User("User1", "Password", "djdjjdj", files); + appData.setAppConfig(appConfig); + appData.addUser(user1); + JsonAdapter jsonAdapter = new JsonAdapter(appData); + //WHEN Repository calls save method + String filePath = "src/test/resources/test.json"; + Repository.save(filePath, jsonAdapter); + //THEN + assertTrue(Files.exists(Paths.get("src/test/resources/test.json").toAbsolutePath())); + } + + @Test + public void load() { + //GIVEN a test json file loadTest.json + JsonAdapter jsonAdapter = new JsonAdapter(null); + //WHEN repository calls load method + AppData appData = Repository.load("src/test/resources/loadTest.json", jsonAdapter); + //THEN + assertNotNull(appData.getAppConfig()); + assertEquals("My network interface", appData.getAppConfig().getNetworkInterface()); + assertEquals(32000, appData.getAppConfig().getUnicastPort()); + assertEquals("224.25.0.1", appData.getAppConfig().getMulticastIp()); + assertEquals(15502, appData.getAppConfig().getMulticastPort()); + assertFalse(appData.getAppConfig().isTls()); + } +} diff --git a/app/src/test/resources/loadTest.json b/app/src/test/resources/loadTest.json new file mode 100644 index 0000000..f06f3af --- /dev/null +++ b/app/src/test/resources/loadTest.json @@ -0,0 +1,24 @@ +{ + "unicast_port": 32000, + "multicast_ip": "224.25.0.1", + "multicast_port": 15502, + "network_interface": "My network interface", + "tls": false, + "users": [ + { + "name": "User1", + "password": "Password", + "aes_key": "djdjjdj", + "files": [ + { + "name": "File1", + "size": 15, + "iv": "8d8d8d8d", + "storage": [ + "StorBackEnd1" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c..7454180 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index da9702f..69a9715 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..744e882 100644 --- a/gradlew +++ b/gradlew @@ -72,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e41310b --- /dev/null +++ b/settings.gradle @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.1/userguide/multi_project_builds.html + */ + +rootProject.name = 'FileFrontEnd' +include('app')