diff --git a/.gitignore b/.gitignore
index 9267e07..41874ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ gradle-app.setting
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
/out/
+/clavardator_stored_files/
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index a80c414..86ab5b3 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -11,6 +11,7 @@
+
diff --git a/client/config.json b/client/config.json
index 7129e25..6cb0675 100644
--- a/client/config.json
+++ b/client/config.json
@@ -1,7 +1,7 @@
{
"serveur": {
"actif": 1,
- "uri": "test",
+ "uri": "localhost",
"type": "INSA",
"ports": {
"presence": 35650,
diff --git a/client/src/main/java/fr/insa/clavardator/client/errors/UsernameTakenException.java b/client/src/main/java/fr/insa/clavardator/client/errors/UsernameTakenException.java
deleted file mode 100644
index 21b6c37..0000000
--- a/client/src/main/java/fr/insa/clavardator/client/errors/UsernameTakenException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package fr.insa.clavardator.client.errors;
-
-import java.io.Serializable;
-
-public class UsernameTakenException extends Exception implements Serializable {
- public UsernameTakenException(String message) {
- super(message);
- }
-}
diff --git a/client/src/main/java/fr/insa/clavardator/client/network/PeerHandshake.java b/client/src/main/java/fr/insa/clavardator/client/network/PeerHandshake.java
index ce829cd..dece5c0 100644
--- a/client/src/main/java/fr/insa/clavardator/client/network/PeerHandshake.java
+++ b/client/src/main/java/fr/insa/clavardator/client/network/PeerHandshake.java
@@ -1,7 +1,7 @@
package fr.insa.clavardator.client.network;
-import fr.insa.clavardator.client.errors.UsernameTakenException;
import fr.insa.clavardator.client.users.CurrentUser;
+import fr.insa.clavardator.lib.errors.UsernameTakenException;
import fr.insa.clavardator.lib.network.TcpConnection;
import fr.insa.clavardator.lib.users.UserInformation;
import fr.insa.clavardator.lib.util.ErrorCallback;
@@ -71,7 +71,7 @@ public class PeerHandshake {
private void sendUsernameTaken(TcpConnection thisConnection) {
Log.v(this.getClass().getSimpleName(), "Received username request using current username");
- thisConnection.send(new UsernameTakenException("Username taken"), this::closeConnection, null);
+ thisConnection.send(new UsernameTakenException("Username taken", userInformation.id), this::closeConnection, null);
}
diff --git a/client/src/main/java/fr/insa/clavardator/client/server/InsaPresence.java b/client/src/main/java/fr/insa/clavardator/client/server/InsaPresence.java
index 9c0e4ad..13df66e 100644
--- a/client/src/main/java/fr/insa/clavardator/client/server/InsaPresence.java
+++ b/client/src/main/java/fr/insa/clavardator/client/server/InsaPresence.java
@@ -1,8 +1,8 @@
package fr.insa.clavardator.client.server;
-import fr.insa.clavardator.lib.network.TcpConnection;
import fr.insa.clavardator.client.users.CurrentUser;
import fr.insa.clavardator.lib.message.Message;
+import fr.insa.clavardator.lib.network.TcpConnection;
import fr.insa.clavardator.lib.users.UserInformation;
import fr.insa.clavardator.lib.util.ErrorCallback;
import fr.insa.clavardator.lib.util.Log;
@@ -83,14 +83,19 @@ public class InsaPresence implements Presence {
final ArrayList> list = (ArrayList>) msg;
for (Object obj : list) {
if (obj instanceof UserInformation) {
- userList.add((UserInformation)obj);
+ userList.add((UserInformation) obj);
}
}
- }
- Log.v(getClass().getSimpleName(), "Receive subscribe response: " + msg);
- Log.v(getClass().getSimpleName(), "Converted list: " + Arrays.toString(userList.toArray()));
- if (callback != null) {
- callback.call(userList);
+ Log.v(getClass().getSimpleName(), "Receive subscribe response: " + msg);
+ Log.v(getClass().getSimpleName(), "Converted list: " + Arrays.toString(userList.toArray()));
+ if (callback != null) {
+ callback.call(userList);
+ }
+ } else {
+ Log.e(getClass().getSimpleName(), "Unexpected object received: " + msg.getClass().getSimpleName());
+ if (errorCallback != null) {
+ errorCallback.onError(new RuntimeException("Unexpected object received"));
+ }
}
}, errorCallback);
}
@@ -133,13 +138,12 @@ public class InsaPresence implements Presence {
*/
private void connectToProxy(SimpleCallback callback, ErrorCallback errorCallback) {
try {
- proxyConnection = new TcpConnection(InetAddress.getByName(path),
- proxyPort,
- (newConnection) -> {
+ proxyConnection = new TcpConnection(InetAddress.getByName(path), proxyPort,
+ (newConnection) -> newConnection.send(new UserInformation(CurrentUser.getInstance()), () -> {
if (callback != null) {
callback.call();
}
- },
+ }, errorCallback),
errorCallback);
} catch (UnknownHostException e) {
Log.e(getClass().getSimpleName(), "Could not connect to presence proxy", e);
diff --git a/client/src/main/java/fr/insa/clavardator/client/users/PeerUser.java b/client/src/main/java/fr/insa/clavardator/client/users/PeerUser.java
index 053a52a..4343431 100644
--- a/client/src/main/java/fr/insa/clavardator/client/users/PeerUser.java
+++ b/client/src/main/java/fr/insa/clavardator/client/users/PeerUser.java
@@ -1,10 +1,10 @@
package fr.insa.clavardator.client.users;
import fr.insa.clavardator.client.chat.ChatHistory;
+import fr.insa.clavardator.client.db.DatabaseController;
+import fr.insa.clavardator.lib.errors.UsernameTakenException;
import fr.insa.clavardator.lib.message.FileMessage;
import fr.insa.clavardator.lib.message.Message;
-import fr.insa.clavardator.client.db.DatabaseController;
-import fr.insa.clavardator.client.errors.UsernameTakenException;
import fr.insa.clavardator.lib.network.TcpConnection;
import fr.insa.clavardator.lib.users.User;
import fr.insa.clavardator.lib.users.UserInformation;
@@ -102,7 +102,7 @@ public class PeerUser extends User implements Comparable {
private void sendUsernameTaken(TcpConnection thisConnection) {
Log.v(this.getClass().getSimpleName(), "Received username request using current username");
- thisConnection.send(new UsernameTakenException("Username taken"), this::disconnect, null);
+ thisConnection.send(new UsernameTakenException("Username taken", getId()), this::disconnect, null);
}
public void init(TcpConnection connection, String id, String username, ErrorCallback errorCallback) {
diff --git a/client/src/main/java/fr/insa/clavardator/client/users/UserList.java b/client/src/main/java/fr/insa/clavardator/client/users/UserList.java
index 9d13a33..97a0aba 100644
--- a/client/src/main/java/fr/insa/clavardator/client/users/UserList.java
+++ b/client/src/main/java/fr/insa/clavardator/client/users/UserList.java
@@ -54,7 +54,7 @@ public class UserList {
if (savedUser.isActive()) {
Log.v(getClass().getSimpleName(), "Received user from presence server already known and connected");
} else {
- Log.v(getClass().getSimpleName(), "Received user from presence server already known and connected");
+ Log.v(getClass().getSimpleName(), "Received user from presence server already known but not connected, connecting...");
savedUser.init(proxyConnection, userInfo.id, userInfo.getUsername(), null);
}
} else {
diff --git a/lib/src/main/java/fr/insa/clavardator/lib/errors/UsernameTakenException.java b/lib/src/main/java/fr/insa/clavardator/lib/errors/UsernameTakenException.java
new file mode 100644
index 0000000..af2c2ae
--- /dev/null
+++ b/lib/src/main/java/fr/insa/clavardator/lib/errors/UsernameTakenException.java
@@ -0,0 +1,12 @@
+package fr.insa.clavardator.lib.errors;
+
+import java.io.Serializable;
+
+public class UsernameTakenException extends Exception implements Serializable {
+ public final String recipient;
+
+ public UsernameTakenException(String message, String recipient) {
+ super(message);
+ this.recipient = recipient;
+ }
+}
diff --git a/lib/src/main/java/fr/insa/clavardator/lib/network/TcpListener.java b/lib/src/main/java/fr/insa/clavardator/lib/network/TcpListener.java
index fb5ce7b..d32eb86 100644
--- a/lib/src/main/java/fr/insa/clavardator/lib/network/TcpListener.java
+++ b/lib/src/main/java/fr/insa/clavardator/lib/network/TcpListener.java
@@ -10,10 +10,16 @@ import static fr.insa.clavardator.lib.network.TcpConnection.TCP_PORT;
public class TcpListener {
Acceptor acceptor = null;
+ int port = TCP_PORT;
public TcpListener() {
}
+ public TcpListener(int port) {
+ this.port = port;
+ }
+
+
/**
* Start accepting incoming connections
*
@@ -24,7 +30,7 @@ public class TcpListener {
if (acceptor != null) {
acceptor.stopAccepting();
}
- acceptor = new Acceptor(callback, errorCallback);
+ acceptor = new Acceptor(port, callback, errorCallback);
acceptor.start();
}
@@ -43,8 +49,10 @@ public class TcpListener {
private final NewConnectionCallback callback;
private final ErrorCallback errorCallback;
private ServerSocket server;
+ private int port;
- public Acceptor(NewConnectionCallback callback, ErrorCallback errorCallback) {
+ public Acceptor(int port, NewConnectionCallback callback, ErrorCallback errorCallback) {
+ this.port = port;
this.callback = callback;
this.errorCallback = errorCallback;
}
@@ -52,7 +60,7 @@ public class TcpListener {
@Override
public void run() {
try {
- server = new ServerSocket(TCP_PORT);
+ server = new ServerSocket(port);
while (!shouldStop) {
Socket clientSocket = server.accept();
callback.onNewConnection(clientSocket);
diff --git a/server/build.gradle b/server/build.gradle
new file mode 100644
index 0000000..15511c3
--- /dev/null
+++ b/server/build.gradle
@@ -0,0 +1,18 @@
+plugins {
+ id 'application'
+ id 'com.github.johnrengelman.shadow' version '6.1.0'
+}
+
+group 'fr.insa.clavardator.server'
+version '0.0.1'
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation project(':lib')
+ implementation 'org.jetbrains:annotations:20.1.0'
+}
+
+mainClassName = 'fr.insa.clavardator.server.Main'
\ No newline at end of file
diff --git a/server/src/main/java/fr/insa/clavardator/server/Main.java b/server/src/main/java/fr/insa/clavardator/server/Main.java
new file mode 100644
index 0000000..a122782
--- /dev/null
+++ b/server/src/main/java/fr/insa/clavardator/server/Main.java
@@ -0,0 +1,24 @@
+package fr.insa.clavardator.server;
+
+import fr.insa.clavardator.lib.util.Log;
+
+import java.util.Scanner;
+
+public class Main {
+
+ public static void main(String[] args) {
+ Log.v(Main.class.getSimpleName(), "Server started! You can stop it by typing stop");
+
+ Proxy proxy = new Proxy();
+ Presence presence = new Presence();
+
+ String line;
+ Scanner scanner = new Scanner(System.in);
+ do {
+ line = scanner.nextLine();
+ } while (!line.equals("stop"));
+
+ proxy.stop();
+ presence.stop();
+ }
+}
diff --git a/server/src/main/java/fr/insa/clavardator/server/Presence.java b/server/src/main/java/fr/insa/clavardator/server/Presence.java
new file mode 100644
index 0000000..32321b8
--- /dev/null
+++ b/server/src/main/java/fr/insa/clavardator/server/Presence.java
@@ -0,0 +1,87 @@
+package fr.insa.clavardator.server;
+
+import fr.insa.clavardator.lib.network.TcpConnection;
+import fr.insa.clavardator.lib.network.TcpListener;
+import fr.insa.clavardator.lib.users.UserInformation;
+import fr.insa.clavardator.lib.util.Log;
+
+import java.io.EOFException;
+import java.io.Serializable;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Presence {
+ private static final int PRESENCE_PORT = 35650;
+
+ private final Map subscribers = new HashMap<>();
+ private final ArrayList connectedUsers = new ArrayList<>();
+ private final TcpListener presenceListener;
+
+ public Presence() {
+ presenceListener = new TcpListener(PRESENCE_PORT);
+ presenceListener.acceptConnection(this::subscribe,
+ e -> Log.e(getClass().getSimpleName(), "Error while registering a user", e));
+ }
+
+ public void publish(UserInformation info) {
+ for (TcpConnection subscriber : subscribers.values()) {
+ ArrayList msg = new ArrayList<>(1);
+ msg.add(info);
+ subscriber.send(msg, null,
+ e -> Log.e(getClass().getSimpleName(), "Error while publishing user information", e));
+ }
+ }
+
+ public void subscribe(Socket socket) {
+ TcpConnection user = new TcpConnection(socket);
+
+ // Receive user information
+ user.receiveOne(o -> {
+ if (o instanceof UserInformation) {
+ UserInformation userInformation = ((UserInformation) o);
+ Log.v(getClass().getSimpleName(), "Registering user " + userInformation.id +
+ " (" + userInformation.getUsername() + ")");
+
+ // publish new user info to all subscribers
+ publish(userInformation);
+
+ // Send the list of connected users to the new subscriber. We're cloning it because we're modifying it
+ // just after this call, while the sender thread might not have send it yet
+ user.send((Serializable) connectedUsers.clone(), null,
+ e -> Log.e(getClass().getSimpleName(), "Error while receiving user information", e));
+
+ subscribers.put(userInformation.id, user);
+ connectedUsers.add(userInformation);
+
+ user.receive(msg -> Log.w(getClass().getSimpleName(), "Received an unexpected message " + msg),
+ e -> {
+ if (e instanceof EOFException) {
+ unsubscribe(userInformation.id);
+ }
+ });
+ } else {
+ Log.e(getClass().getSimpleName(), "Received unexpected message type: " + o.getClass().getSimpleName());
+ }
+ }, e -> {
+ if (!(e instanceof EOFException)) {
+ Log.e(getClass().getSimpleName(), "Error while receiving user information", e);
+ }
+ });
+ }
+
+ public void unsubscribe(String id) {
+ Log.v(getClass().getSimpleName(), "Unsubscribing user " + id);
+ subscribers.get(id).close();
+ subscribers.remove(id);
+ connectedUsers.removeIf(userInformation -> userInformation.id.equals(id));
+ }
+
+ public void stop() {
+ presenceListener.stopAccepting();
+ for (TcpConnection connection : subscribers.values()) {
+ connection.close();
+ }
+ }
+}
diff --git a/server/src/main/java/fr/insa/clavardator/server/Proxy.java b/server/src/main/java/fr/insa/clavardator/server/Proxy.java
new file mode 100644
index 0000000..f9aebc9
--- /dev/null
+++ b/server/src/main/java/fr/insa/clavardator/server/Proxy.java
@@ -0,0 +1,80 @@
+package fr.insa.clavardator.server;
+
+import fr.insa.clavardator.lib.errors.UsernameTakenException;
+import fr.insa.clavardator.lib.message.Message;
+import fr.insa.clavardator.lib.network.TcpConnection;
+import fr.insa.clavardator.lib.network.TcpListener;
+import fr.insa.clavardator.lib.users.UserInformation;
+import fr.insa.clavardator.lib.util.Log;
+
+import java.io.EOFException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Proxy {
+ private static final int PROXY_PORT = 35750;
+ private final HashMap users = new HashMap<>();
+ private final TcpListener proxyListener;
+
+ public Proxy() {
+ proxyListener = new TcpListener(PROXY_PORT);
+ proxyListener.acceptConnection(clientSocket -> {
+
+ Log.v(getClass().getSimpleName(), "Accepting a new user");
+ TcpConnection connection = new TcpConnection(clientSocket);
+ connection.receive(msg -> {
+
+ if (msg instanceof Message) {
+ Log.v(getClass().getSimpleName(), "Transmitting message: " + msg);
+ transmitMessage((Serializable) msg, ((Message) msg).getRecipient().id);
+
+ } else if (msg instanceof UsernameTakenException) {
+ UsernameTakenException unameTaken = ((UsernameTakenException) msg);
+ transmitMessage(unameTaken, unameTaken.recipient);
+
+ } else if (msg instanceof UserInformation) {
+ Log.v(getClass().getSimpleName(), "Registering new user: " + msg);
+ users.put(((UserInformation) msg).id, connection);
+ for (String userId : users.keySet()) {
+ UserInformation userInfo = ((UserInformation) msg);
+ if (!userId.equals(userInfo.id)) {
+ transmitMessage((Serializable) msg, userId);
+ }
+ }
+ }
+
+ }, e -> {
+ if (e instanceof EOFException) {
+ for (Map.Entry user : users.entrySet()) {
+ if (user.getValue().equals(connection)) {
+ Log.v(getClass().getSimpleName(), "Disconnecting user " + user.getKey());
+ users.remove(user.getKey());
+ break;
+ }
+ }
+ } else {
+ Log.e(getClass().getSimpleName(), "Error while receiving message to transmit", e);
+ }
+ });
+ },
+ e -> Log.e(getClass().getSimpleName(), "Error while accepting a user", e));
+ }
+
+
+ void transmitMessage(Serializable msg, String recipientId) {
+ TcpConnection user = users.get(recipientId);
+ if (user == null) {
+ Log.e(getClass().getSimpleName(), "Cannot find the recipient in the connected users");
+ } else {
+ user.send(msg, null, e -> Log.e(getClass().getSimpleName(), "Error while sending message", e));
+ }
+ }
+
+ public void stop() {
+ proxyListener.stopAccepting();
+ for (TcpConnection connection : users.values()) {
+ connection.close();
+ }
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index 8449312..67eb568 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,4 @@
rootProject.name = 'clavardator'
include 'client'
include 'lib'
+include 'server'