@@ -4,6 +4,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.rmi.ConnectException;
@@ -22,9 +23,14 @@

public class Server implements ServerInterface {

// Ressources pouvant etre potentiellement etre utilisees de facon concurentielle par les differents threads sur le serveur
Map<String, String> users = new HashMap<String, String>();
Map<String, String> filesAndLocks = new HashMap<String, String>();

// Objets bidons permettant de faire des locks sur les ressources partagees
Object lockUsers = new Object();
Object lockFiles = new Object();

private final static String FILES_DIRECTORY_NAME = "./FilesDirectory/";
private final static String METADATA_DIRECTORY_NAME = "./MetaDataDirectory/";
private final static String CREDENTIALS_METADATA_FILE = "credentials.txt";
@@ -75,7 +81,10 @@ private void initializeFilesAndLocks()
if(files!=null)
{
for(File aFile : files) {
filesAndLocks.put(aFile.getName(), "");
synchronized(lockFiles) {
// acces a filesAndLocks ici est thread safe
filesAndLocks.put(aFile.getName(), "");
}
}
}
}
@@ -92,17 +101,23 @@ private void initializeMeta(String filename)
String fileContent = new String(Files.readAllBytes(Paths.get(METADATA_DIRECTORY_NAME + filename)));
Map<String, String> map = parseMetaMap(fileContent);
if (filename.equals(CREDENTIALS_METADATA_FILE)) {
users = map;
synchronized(lockUsers) {
// L'acces a users ici est thread safe
users = map;
}
} else if (filename.equals(LOCK_METADATA_FILE)) {
// TODO : debug this (verify that this gets filled up properly : bug on list)
filesAndLocks = map;
synchronized(lockFiles) {
// L'acces a filesAndLocks ici est thread safe
filesAndLocks = map;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

private void run() {
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
@@ -127,24 +142,36 @@ private void run() {

/*
* Méthode accessible par RMI. Return false if the user already exists.
* Cette methode est thread safe et va bloquer les autres thread si un thread est deja entrain
* dacceder a la methode.
*/
@Override
public boolean newUser(String login, String password) throws RemoteException {
public synchronized boolean newUser(String login, String password) throws RemoteException {
if(users.get(login) == null)
{
//access a users ici est thread safe
users.put(login, password);

saveMetaToFile(users, CREDENTIALS_METADATA_FILE);
return true;
}
return false;
}

public boolean verify(List<String> credentials) throws RemoteException {
/*
* Cette methode verifie que les informations dauthentification fournies sont valides.
*/
private boolean verify(List<String> credentials) throws RemoteException {
return credentials.get(1).equals(users.get(credentials.get(0)));
}

/*
* Méthode accessible par RMI permettant de creer un utilisateur.
* Cette methode est thread safe, c-a-d quelle bloque lorsquun thread essaie de lui acceder
* lorsquelle est utilisee.
*/
@Override
public boolean create(String fileName, List<String> credentials) throws RemoteException {
public synchronized boolean create(String fileName, List<String> credentials) throws RemoteException {
if(!verify(credentials))
{
throw new RemoteException("Invalid credentials for user " + credentials.get(0));
@@ -157,6 +184,7 @@ public boolean create(String fileName, List<String> credentials) throws RemoteEx
if(file.createNewFile())
{
filesAndLocks.put(fileName, "");

saveMetaToFile(filesAndLocks, LOCK_METADATA_FILE);
return true;
}
@@ -170,6 +198,10 @@ public boolean create(String fileName, List<String> credentials) throws RemoteEx
}
}

/*
* Méthode accessible par RMI permettant de lister les fichiers et les
* utilisateurs qui possedent un lock dessus.
*/
@Override
public Map<String, String> list(List<String> credentials) throws RemoteException {
if(!verify(credentials))
@@ -180,8 +212,14 @@ public Map<String, String> list(List<String> credentials) throws RemoteException
return filesAndLocks;
}

/*
* Méthode accessible par RMI (thread safe) qui construit une map avec le nom de chaque
* fichier et leur contenu et retourne cette map.
* Cette methode est thread safe pour sassurer quil ny ai pas de corruption au niveau de
* filesAndContent par exemple.
*/
@Override
public Map<String, String> syncLocalDirectory(List<String> credentials) throws RemoteException {
public synchronized Map<String, String> syncLocalDirectory(List<String> credentials) throws RemoteException {
if(!verify(credentials))
{
throw new RemoteException("Invalid credentials for user " + credentials.get(0));
@@ -201,49 +239,63 @@ public Map<String, String> syncLocalDirectory(List<String> credentials) throws R
return filesAndContent;
}

/*
* Méthode permettant de recuperer le contenu dun fichier donne.
* La methode est thread safe pour eviter la corruption au niveau de certaines structures de donnnes
* dans la methode tel que les byte arrays.
*/
@Override
public String get(String fileName, byte[] checksum, List<String> credentials) throws RemoteException {
public synchronized String get(String fileName, byte[] checksum, List<String> credentials) throws RemoteException {
if(!verify(credentials))
{
throw new RemoteException("Invalid credentials for user " + credentials.get(0));
}

String fileContent = null;
try {
byte[] b = Files.readAllBytes(Paths.get(FILES_DIRECTORY_NAME+fileName));
byte[] hash = MessageDigest.getInstance("MD5").digest(b);

if(Arrays.equals(hash, checksum))
{
return null;
}
else
if(!Arrays.equals(hash, checksum))
{
return new String(b);
fileContent = new String(b);
}


} catch (IOException | NoSuchAlgorithmException e) {
// TODO gerer le cas ou le nom du fichier est inexistant
} catch (NoSuchFileException e) {
throw new RemoteException("Le fichier demande" + fileName + "nexiste pas.");
}
catch (IOException | NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RemoteException(e.getMessage());
}
return null;

return fileContent;
}

/*
* Méthode accessible par RMI permettant de locker un fichier.
* Cette methode retourne une map contenant un element avec le nom de lutilisateur du fichier (lock)
* ainsi que le contenu du fichier a jour. Cette methode est thread safe.
*/
@Override
public Map<String, String> lock(String fileName, byte[] checksum, List<String> credentials) throws RemoteException {
public synchronized Map<String, String> lock(String fileName, byte[] checksum, List<String> credentials) throws RemoteException {
if(!verify(credentials))
{
throw new RemoteException("Invalid credentials for user " + credentials.get(0));
}

// check if file exists
// regarder si le fichier existe
File f = new File(FILES_DIRECTORY_NAME + fileName);
if (f.exists() && !f.isDirectory()) {
// check if file is already locked by another client
// regarder si le fichier est lock par un autre client
String currentUser = filesAndLocks.get(fileName);

// map contenant un element : lutilisateur (lock) du fichier, ainsi que le contenu du fichier
Map<String, String> infos = new HashMap<String, String>();
if (currentUser.equals("")) {
filesAndLocks.put(fileName, credentials.get(0));

infos.put(credentials.get(0), get(fileName, checksum, credentials));
}
else // dont update file content if the file is locked by other user
@@ -260,29 +312,29 @@ public Map<String, String> lock(String fileName, byte[] checksum, List<String> c
}
}

/*
* Méthode accessible par RMI permettant de mettre un fichier sur le serveur.
* Cette methode est thread safe.
*/
@Override
public void push(String fileName, String content, List<String> credentials) throws RemoteException {
public synchronized void push(String fileName, String content, List<String> credentials) throws RemoteException {
if(!verify(credentials))
{
throw new RemoteException("Invalid credentials for user " + credentials.get(0));
}

// TODO check if we need to verify more here instead than in the client (lock)
try {

// check if file exists
// verifier si le fichier existe, que ce nest pas un directory et que lutilisateur a le lock sur le fichier
File f = new File(FILES_DIRECTORY_NAME + fileName);
if (f.exists() && !f.isDirectory()) {
// check if file is already locked by another client
String currentUser = filesAndLocks.get(fileName);
if (currentUser.equals(credentials.get(0))) {
System.out.println("writing content to file" + content + "content.len" + content.length() );
Files.write(Paths.get(FILES_DIRECTORY_NAME + fileName), content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);

filesAndLocks.put(fileName, "");
}
} else {
if (f.exists() && !f.isDirectory() && filesAndLocks.get(fileName).equals(credentials.get(0))) {
System.out.println("writing content to file" + content + "content.len" + content.length() );
Files.write(Paths.get(FILES_DIRECTORY_NAME + fileName), content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
filesAndLocks.put(fileName, "");
} else if (!f.exists()) {
throw new RemoteException(fileName + " existe pas.");
} else if (!filesAndLocks.get(fileName).equals(credentials.get(0))) {
throw new RemoteException("Vous navez pas de lock sur le fichier. Vous devez prealablement lavoir pour faire un push.");
}

// sauvegarder un fichier de metadonnees des locks respectifs des fichiers
@@ -292,7 +344,8 @@ public void push(String fileName, String content, List<String> credentials) thro
e.printStackTrace();
}
}



/*
* Methode permettant de sauvegarder des donnees provenant dune hashmap dans un fichier
* contenant les meta donnees (soit les informations des credentials ou celles des fichiers
@@ -344,4 +397,6 @@ private Map<String, String> parseMetaMap(String metaMap) {
return meta;
}



}