[WIP] Integrate server to the project

Many bugs are yet to fix
This commit is contained in:
Yohan Simard 2021-01-20 14:26:57 +01:00
parent 7bbaa4f8da
commit 32364461be
15 changed files with 257 additions and 30 deletions

1
.gitignore vendored
View file

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

View file

@ -11,6 +11,7 @@
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/client" />
<option value="$PROJECT_DIR$/lib" />
<option value="$PROJECT_DIR$/server" />
</set>
</option>
</GradleProjectSettings>

View file

@ -1,7 +1,7 @@
{
"serveur": {
"actif": 1,
"uri": "test",
"uri": "localhost",
"type": "INSA",
"ports": {
"presence": 35650,

View file

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

View file

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

View file

@ -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,8 +83,7 @@ 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);
@ -92,6 +91,12 @@ public class InsaPresence implements Presence {
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);

View file

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

View file

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

View file

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

View file

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

18
server/build.gradle Normal file
View file

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

View file

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

View file

@ -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<String, TcpConnection> subscribers = new HashMap<>();
private final ArrayList<UserInformation> 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<UserInformation> 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();
}
}
}

View file

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

View file

@ -1,3 +1,4 @@
rootProject.name = 'clavardator'
include 'client'
include 'lib'
include 'server'