Merge branch 'dev'

# Conflicts:
#	.idea/compiler.xml
#	.idea/discord.xml
#	.idea/misc.xml
#	gradle/wrapper/gradle-wrapper.jar
#	gradle/wrapper/gradle-wrapper.properties
#	gradlew
This commit is contained in:
Benjamin 2022-03-08 19:46:07 +01:00
commit 4974b64e18
64 changed files with 3141 additions and 5 deletions

6
.gitattributes vendored Normal file
View File

@ -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

160
.gitignore vendored Normal file
View File

@ -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

8
.idea/.gitignore vendored Normal file
View File

@ -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/

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.6" /> <bytecodeTargetLevel target="11" />
</component> </component>
</project> </project>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="DiscordProjectSettings"> <component name="DiscordProjectSettings">
<option name="show" value="ASK" /> <option name="show" value="PROJECT_FILES" />
<option name="description" value="" /> <option name="description" value="" />
</component> </component>
</project> </project>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="InfiniteLoopStatement" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

20
.idea/jarRepositories.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
</component>
</project>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="libraries-with-intellij-classes">
<option name="intellijApiContainingLibraries">
<list>
<LibraryCoordinatesState>
<option name="artifactId" value="ideaIU" />
<option name="groupId" value="com.jetbrains.intellij.idea" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="ideaIU" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="ideaIC" />
<option name="groupId" value="com.jetbrains.intellij.idea" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="ideaIC" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="pycharmPY" />
<option name="groupId" value="com.jetbrains.intellij.pycharm" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="pycharmPY" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="pycharmPC" />
<option name="groupId" value="com.jetbrains.intellij.pycharm" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="pycharmPC" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="clion" />
<option name="groupId" value="com.jetbrains.intellij.clion" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="clion" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="riderRD" />
<option name="groupId" value="com.jetbrains.intellij.rider" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="riderRD" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="goland" />
<option name="groupId" value="com.jetbrains.intellij.goland" />
</LibraryCoordinatesState>
<LibraryCoordinatesState>
<option name="artifactId" value="goland" />
<option name="groupId" value="com.jetbrains" />
</LibraryCoordinatesState>
</list>
</option>
</component>
</project>

View File

@ -4,5 +4,5 @@
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" /> <file type="web" url="file://$PROJECT_DIR$" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" project-jdk-name="11" project-jdk-type="JavaSDK" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK" />
</project> </project>

37
app/build.gradle Normal file
View File

@ -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()
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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 <j.nihart@student.helmo.be>
*/
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;
}
}

View File

@ -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 <j.nihart@student.helmo.be>
*/
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;
}
}

View File

@ -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
* <p>
* Class listening to the announcement of new StoreBackEnd.
* Allowing it to be used as a storage unit.
*
* @author Jérémi NIHART <j.nihart@student.helmo.be>
* @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();
}
}

View File

@ -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) { }
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -0,0 +1,15 @@
package lightcontainer.interfaces;
import lightcontainer.protocol.ProtocolReader;
import lightcontainer.protocol.ProtocolWriter;
public interface ProtocolRepository {
<T extends ProtocolReader.ProtocolResult> T executeReader(String data);
<T extends ProtocolWriter.ProtocolResult> T executeWriter(String cmdName, String... data);
void addReader(ProtocolReader reader);
void addWriter(ProtocolWriter writer);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 extends ProtocolResult> 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> T onExecuted(String... data);
}

View File

@ -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 extends ProtocolResult> 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 extends ProtocolResult> T onExecuted(String... data) {
return (T) new ProtocolResult();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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]);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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 <j.nihart@student.helmo.be>
*/
public class ClientHandlerRepository implements AutoCloseable, UnicastCHR {
// Variable
private final List<ClientHandler> 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);
}
}

View File

@ -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<Task> 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<Task> 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<Task> 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);
}
}

View File

@ -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<ProtocolReader> readers = new HashSet<>();
private final Set<ProtocolWriter> writers = new HashSet<>();
@Override
public <T extends ProtocolReader.ProtocolResult> T executeReader(String data) {
for (ProtocolReader reader : readers) {
T readerResult = reader.execute(data);
if (readerResult != null) {
return readerResult;
}
}
return null;
}
@Override
public <T extends ProtocolWriter.ProtocolResult> 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);
}
}

View File

@ -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 <j.nihart@student.helmo.be>
*/
public class StoreProcessorRepository implements AutoCloseable, MulticastSPR {
// Variables
private final Set<StoreProcessor> 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);
}
}

View File

@ -0,0 +1,8 @@
package lightcontainer.storage;
public interface Adapter {
String toString();
AppData fromString(String appDataString);
}

View File

@ -0,0 +1,88 @@
package lightcontainer.storage;
/**
* AppConfig represents all network related information needed for the program to work.
*
* @author Maximilien LEDOUX <m.ledoux@student.helmo.be>
* @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;
}
}

View File

@ -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 <m.ledoux@student.helmo.be>
* @version 1.0
* @see AppConfig
* @see User
* @since 1.0
*/
public class AppData {
private static AppData instance = null;
private AppConfig appConfig;
private final Map<String, User> 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<User> 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.
* <p>
* 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);
}
}
}

View File

@ -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<String> storage;
public File(String name, int size, String iv, Set<String> 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<String> getStorageIterator() {
return storage.iterator();
}
public boolean addStorage(String storage) {
if (this.storage.contains(storage)) {
return false;
} else {
this.storage.add(storage);
return true;
}
}
}

View File

@ -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<User> userIterator = appData.usersIterator();
addUsers(users, userIterator);
config.add("users", users);
return config.toString();
}
private void addUsers(JsonArray users, Iterator<User> 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<File> fileIterator = current.fileIterator();
addFiles(fileIterator, files);
user.add("files", files);
users.add(user);
}
}
private void addFiles(Iterator<File> 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<String> storageIterator = currentFile.getStorageIterator();
addStorage(storage, storageIterator);
file.add("storage", storage);
files.add(file);
}
}
private void addStorage(JsonArray storage, Iterator<String> 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<User> 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<User> 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<String, File> 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<String, File> 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<String> 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<String> storage, JsonArray jsonStorage) {
for (JsonElement storageElement : jsonStorage) {
String storageName = storageElement.getAsString();
storage.add(storageName);
}
}
}

View File

@ -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();
}
}

View File

@ -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 <m.ledoux@student.helmo.be>
* @version 1.0
* @since 1.0
*/
public class User {
private final String Name;
private final String password;
private final String aesKey;
private final Map<String, File> files;
public User(String Name, String password, String aesKey, Map<String, File> 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<File> 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;
}
}
}

View File

@ -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": []
}
]
}

View File

@ -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));
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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<NetworkInterface> 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<NetworkInterface> discoveredInterfaces = NetworkInterface.getNetworkInterfaces();
while (discoveredInterfaces.hasMoreElements()) {
NetworkInterface currentInterface = discoveredInterfaces.nextElement();
Enumeration<InetAddress> 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();
}
}

View File

@ -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$

View File

@ -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");
// }
}

View File

@ -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());
}
}

View File

@ -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));
}
}

View File

@ -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<String, File> files = new HashMap<>();
Set<String> 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());
}
}

View File

@ -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<String, File> files = new HashMap<>();
Set<String> 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());
}
}

View File

@ -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"
]
}
]
}
]
}

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

2
gradlew vendored
View File

@ -72,7 +72,7 @@ case "`uname`" in
Darwin* ) Darwin* )
darwin=true darwin=true
;; ;;
MINGW* ) MSYS* | MINGW* )
msys=true msys=true
;; ;;
NONSTOP* ) NONSTOP* )

11
settings.gradle Normal file
View File

@ -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')