Merge branch 'maximilien' into dev

# Conflicts:
#	app/src/main/resources/rules.txt
This commit is contained in:
Maximilien LEDOUX 2022-03-08 14:51:38 +01:00
commit d680a85bf2
13 changed files with 735 additions and 2 deletions

View File

@ -22,6 +22,8 @@ dependencies {
// 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 {

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

@ -1,7 +1,6 @@
package lightcontainer.protocol.rules.reader;
import lightcontainer.protocol.ProtocolReader;
import lightcontainer.protocol.rules.reader.HelloRule;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

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