Browse Source

Fix client connection with the proxy

Yohan Simard 3 years ago
parent
commit
f41c7baa8b
20 changed files with 417 additions and 194 deletions
  1. 19
    16
      client/src/main/java/fr/insa/clavardator/client/network/NetDiscoverer.java
  2. 3
    1
      client/src/main/java/fr/insa/clavardator/client/network/PeerHandshake.java
  3. 7
    32
      client/src/main/java/fr/insa/clavardator/client/server/InsaPresence.java
  4. 3
    4
      client/src/main/java/fr/insa/clavardator/client/server/Presence.java
  5. 86
    0
      client/src/main/java/fr/insa/clavardator/client/server/Proxy.java
  6. 5
    5
      client/src/main/java/fr/insa/clavardator/client/ui/MainController.java
  7. 52
    0
      client/src/main/java/fr/insa/clavardator/client/users/DirectPeerConnection.java
  8. 34
    0
      client/src/main/java/fr/insa/clavardator/client/users/PeerConnection.java
  9. 73
    86
      client/src/main/java/fr/insa/clavardator/client/users/PeerUser.java
  10. 43
    0
      client/src/main/java/fr/insa/clavardator/client/users/ProxyPeerConnection.java
  11. 4
    4
      client/src/main/java/fr/insa/clavardator/client/users/UserList.java
  12. 17
    2
      lib/src/main/java/fr/insa/clavardator/lib/errors/UsernameTakenException.java
  13. 3
    2
      lib/src/main/java/fr/insa/clavardator/lib/message/FileMessage.java
  14. 11
    1
      lib/src/main/java/fr/insa/clavardator/lib/message/Message.java
  15. 5
    0
      lib/src/main/java/fr/insa/clavardator/lib/message/RecipientInfo.java
  16. 5
    0
      lib/src/main/java/fr/insa/clavardator/lib/message/SenderInfo.java
  17. 18
    16
      lib/src/main/java/fr/insa/clavardator/lib/network/TcpConnection.java
  18. 2
    2
      lib/src/main/java/fr/insa/clavardator/lib/network/TcpListener.java
  19. 8
    1
      lib/src/main/java/fr/insa/clavardator/lib/users/UserInformation.java
  20. 19
    22
      server/src/main/java/fr/insa/clavardator/server/Proxy.java

+ 19
- 16
client/src/main/java/fr/insa/clavardator/client/network/NetDiscoverer.java View File

@@ -5,7 +5,10 @@ import fr.insa.clavardator.lib.util.Log;
5 5
 import org.jetbrains.annotations.Nullable;
6 6
 
7 7
 import java.io.IOException;
8
-import java.net.*;
8
+import java.net.DatagramPacket;
9
+import java.net.DatagramSocket;
10
+import java.net.InetAddress;
11
+import java.net.SocketException;
9 12
 
10 13
 import static fr.insa.clavardator.client.network.NetUtil.isLocalAddress;
11 14
 
@@ -104,16 +107,22 @@ public class NetDiscoverer {
104 107
 		@Override
105 108
 		public void run() {
106 109
 			byte[] buf = broadcastMessage.getBytes();
110
+			DatagramSocket broadcastSocket = null;
107 111
 			try {
108 112
 				for (InetAddress broadcastAddr : NetUtil.listAllBroadcastAddresses()) {
109
-					DatagramSocket broadcastSocket = new DatagramSocket();
113
+					broadcastSocket = new DatagramSocket();
110 114
 					broadcastSocket.setBroadcast(true);
111 115
 					broadcastSocket.send(new DatagramPacket(buf, buf.length, broadcastAddr, DISCOVERY_PORT));
112 116
 					Log.v(this.getClass().getSimpleName(), "Broadcast sent on address " + broadcastAddr.toString());
117
+					broadcastSocket.close();
113 118
 				}
114 119
 			} catch (IOException e) {
115 120
 				Log.e(this.getClass().getSimpleName(), "Error sending broadcast", e);
116 121
 				errorCallback.onError(e);
122
+			} finally {
123
+				if (broadcastSocket != null) {
124
+					broadcastSocket.close();
125
+				}
117 126
 			}
118 127
 		}
119 128
 	}
@@ -138,15 +147,11 @@ public class NetDiscoverer {
138 147
 		@Override
139 148
 		public void run() {
140 149
 			try {
141
-				while (!shouldStop) {
142
-					socket = new DatagramSocket(null);
143
-					socket.setOption(StandardSocketOptions.SO_REUSEPORT, true);
144
-					socket.setOption(StandardSocketOptions.SO_REUSEADDR, true);
145
-					socket.bind(new InetSocketAddress((InetAddress) null, DISCOVERY_PORT));
150
+				socket = new DatagramSocket(DISCOVERY_PORT);
151
+				byte[] buffer = new byte[BROADCAST_BUFFER_SIZE];
146 152
 
147
-					byte[] buffer = new byte[BROADCAST_BUFFER_SIZE];
153
+				while (!shouldStop) {
148 154
 					DatagramPacket receivedPacket = new DatagramPacket(buffer, BROADCAST_BUFFER_SIZE);
149
-
150 155
 					socket.receive(receivedPacket);
151 156
 					Log.v(this.getClass().getSimpleName(), "Broadcast received from ip " + receivedPacket.getAddress().toString());
152 157
 					callback.onBroadcastReceived(receivedPacket.getAddress(), new String(receivedPacket.getData()));
@@ -156,6 +161,9 @@ public class NetDiscoverer {
156 161
 					Log.e(this.getClass().getSimpleName(), "Error receiving broadcast message", e);
157 162
 					errorCallback.onError(e);
158 163
 				}
164
+			} finally {
165
+				if (socket != null)
166
+					socket.close();
159 167
 			}
160 168
 		}
161 169
 
@@ -186,8 +194,7 @@ public class NetDiscoverer {
186 194
 		@Override
187 195
 		public void run() {
188 196
 			byte[] buf = message.getBytes();
189
-			try {
190
-				DatagramSocket responseSocket = new DatagramSocket();
197
+			try (DatagramSocket responseSocket = new DatagramSocket()) {
191 198
 				responseSocket.send(new DatagramPacket(buf, buf.length, address, RESPONSE_PORT));
192 199
 				Log.v(this.getClass().getSimpleName(), "Broadcast response sent to ip " + address.toString());
193 200
 			} catch (IOException e) {
@@ -217,12 +224,8 @@ public class NetDiscoverer {
217 224
 		@Override
218 225
 		public void run() {
219 226
 			try {
227
+				socket = new DatagramSocket(RESPONSE_PORT);
220 228
 				while (!shouldStop) {
221
-					socket = new DatagramSocket(null);
222
-					socket.setOption(StandardSocketOptions.SO_REUSEPORT, true);
223
-					socket.setOption(StandardSocketOptions.SO_REUSEADDR, true);
224
-					socket.bind(new InetSocketAddress((InetAddress) null, RESPONSE_PORT));
225
-
226 229
 					byte[] buffer = new byte[RESPONSE_BUFFER_SIZE];
227 230
 					DatagramPacket receivedPacket = new DatagramPacket(buffer, RESPONSE_BUFFER_SIZE);
228 231
 					socket.receive(receivedPacket);

+ 3
- 1
client/src/main/java/fr/insa/clavardator/client/network/PeerHandshake.java View File

@@ -71,7 +71,9 @@ public class PeerHandshake {
71 71
 
72 72
 	private void sendUsernameTaken(TcpConnection thisConnection) {
73 73
 		Log.v(this.getClass().getSimpleName(), "Received username request using current username");
74
-		thisConnection.send(new UsernameTakenException("Username taken", userInformation.id), this::closeConnection, null);
74
+		UsernameTakenException exception = new UsernameTakenException("Username taken",
75
+				CurrentUser.getInstance().getId(), userInformation.id);
76
+		thisConnection.send(exception, this::closeConnection, null);
75 77
 	}
76 78
 
77 79
 

+ 7
- 32
client/src/main/java/fr/insa/clavardator/client/server/InsaPresence.java View File

@@ -34,23 +34,20 @@ public class InsaPresence implements Presence {
34 34
 
35 35
 	private final String path;
36 36
 	private final int presencePort;
37
-	private final int proxyPort;
38
-
39 37
 	private TcpConnection presenceConnection;
40
-	private TcpConnection proxyConnection;
41
-
38
+	private final Proxy proxy;
42 39
 
43 40
 	public InsaPresence(String path, int presencePort, int proxyPort) {
44 41
 		this.path = path;
45 42
 		this.presencePort = presencePort;
46
-		this.proxyPort = proxyPort;
43
+		this.proxy = new Proxy(path, proxyPort);
47 44
 	}
48 45
 
49 46
 	@Override
50 47
 	public void subscribe(ParametrizedCallback<ArrayList<UserInformation>> callback, ErrorCallback errorCallback) {
51 48
 		if (!isConnected()) {
52 49
 			connectToPresence(
53
-					() -> connectToProxy(() -> {
50
+					() -> proxy.connect(() -> {
54 51
 						sendSubscribeMessage(errorCallback);
55 52
 						receiveSubscribeNotifications(callback, errorCallback);
56 53
 					}, errorCallback),
@@ -131,26 +128,6 @@ public class InsaPresence implements Presence {
131 128
 	}
132 129
 
133 130
 	/**
134
-	 * Connects to the presence proxy by TCP
135
-	 *
136
-	 * @param callback      Called when connection is successful
137
-	 * @param errorCallback Called on connection error
138
-	 */
139
-	private void connectToProxy(SimpleCallback callback, ErrorCallback errorCallback) {
140
-		try {
141
-			proxyConnection = new TcpConnection(InetAddress.getByName(path), proxyPort,
142
-					(newConnection) -> newConnection.send(new UserInformation(CurrentUser.getInstance()), () -> {
143
-						if (callback != null) {
144
-							callback.call();
145
-						}
146
-					}, errorCallback),
147
-					errorCallback);
148
-		} catch (UnknownHostException e) {
149
-			Log.e(getClass().getSimpleName(), "Could not connect to presence proxy", e);
150
-		}
151
-	}
152
-
153
-	/**
154 131
 	 * Closes the given connection
155 132
 	 */
156 133
 	private void closeConnection(@Nullable TcpConnection c) {
@@ -167,8 +144,7 @@ public class InsaPresence implements Presence {
167 144
 	private boolean isConnected() {
168 145
 		return presenceConnection != null &&
169 146
 				presenceConnection.isOpen() &&
170
-				proxyConnection != null &&
171
-				proxyConnection.isOpen();
147
+				proxy.isConnected();
172 148
 	}
173 149
 
174 150
 	/**
@@ -177,13 +153,12 @@ public class InsaPresence implements Presence {
177 153
 	private void disconnect() {
178 154
 		Log.v(this.getClass().getSimpleName(), "Disconnecting presence server");
179 155
 		closeConnection(presenceConnection);
180
-		closeConnection(proxyConnection);
181 156
 		presenceConnection = null;
182
-		proxyConnection = null;
157
+		proxy.close();
183 158
 	}
184 159
 
185 160
 	@Override
186
-	public TcpConnection getProxyConnection() {
187
-		return proxyConnection;
161
+	public Proxy getProxy() {
162
+		return proxy;
188 163
 	}
189 164
 }

+ 3
- 4
client/src/main/java/fr/insa/clavardator/client/server/Presence.java View File

@@ -1,6 +1,5 @@
1 1
 package fr.insa.clavardator.client.server;
2 2
 
3
-import fr.insa.clavardator.lib.network.TcpConnection;
4 3
 import fr.insa.clavardator.lib.users.UserInformation;
5 4
 import fr.insa.clavardator.lib.util.ErrorCallback;
6 5
 import fr.insa.clavardator.lib.util.ParametrizedCallback;
@@ -36,12 +35,12 @@ public interface Presence {
36 35
 	void unsubscribe(SimpleCallback callback, @Nullable ErrorCallback errorCallback);
37 36
 
38 37
 	/**
39
-	 * Gets a connection to the proxy.
38
+	 * Gets the proxy.
40 39
 	 * This can be used to initialize a
41 40
 	 * {@link fr.insa.clavardator.client.users.PeerUser Peeruser}
42 41
 	 * and send messages like on a local network.
43 42
 	 *
44
-	 * @return The server address
43
+	 * @return The proxy
45 44
 	 */
46
-	TcpConnection getProxyConnection();
45
+	Proxy getProxy();
47 46
 }

+ 86
- 0
client/src/main/java/fr/insa/clavardator/client/server/Proxy.java View File

@@ -0,0 +1,86 @@
1
+package fr.insa.clavardator.client.server;
2
+
3
+import fr.insa.clavardator.client.users.CurrentUser;
4
+import fr.insa.clavardator.lib.message.SenderInfo;
5
+import fr.insa.clavardator.lib.network.TcpConnection;
6
+import fr.insa.clavardator.lib.users.UserInformation;
7
+import fr.insa.clavardator.lib.util.ErrorCallback;
8
+import fr.insa.clavardator.lib.util.Log;
9
+import fr.insa.clavardator.lib.util.SimpleCallback;
10
+import org.jetbrains.annotations.Nullable;
11
+
12
+import java.io.IOException;
13
+import java.io.Serializable;
14
+import java.net.InetAddress;
15
+import java.net.UnknownHostException;
16
+import java.util.HashMap;
17
+import java.util.Map;
18
+
19
+public class Proxy {
20
+	private final String path;
21
+	private final int proxyPort;
22
+	TcpConnection proxyConnection;
23
+	Map<String, TcpConnection.MessageReceivedCallback> callbackMap = new HashMap<>();
24
+	private boolean receiving = false;
25
+
26
+	public Proxy(String path, int proxyPort) {
27
+		this.path = path;
28
+		this.proxyPort = proxyPort;
29
+	}
30
+
31
+	public void connect(SimpleCallback callback, ErrorCallback errorCallback) {
32
+		try {
33
+			proxyConnection = new TcpConnection(InetAddress.getByName(path), proxyPort,
34
+					(newConnection) -> newConnection.send(new UserInformation(CurrentUser.getInstance()), () -> {
35
+						if (callback != null) {
36
+							callback.call();
37
+						}
38
+					}, errorCallback),
39
+					errorCallback);
40
+		} catch (UnknownHostException e) {
41
+			Log.e(getClass().getSimpleName(), "Could not connect to the proxy", e);
42
+		}
43
+	}
44
+
45
+	public void send(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
46
+		proxyConnection.send(message, callback, errorCallback);
47
+	}
48
+
49
+	public void receive(String userId, TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback) {
50
+		Log.v(getClass().getSimpleName(), "Registering a new receive request from " + userId);
51
+		callbackMap.put(userId, callback);
52
+		if (!receiving) {
53
+			proxyConnection.receive(this::onMessageReceived, errorCallback);
54
+			receiving = true;
55
+		}
56
+	}
57
+
58
+	private void onMessageReceived(Object msg) throws IOException {
59
+		if (msg instanceof SenderInfo) {
60
+			TcpConnection.MessageReceivedCallback callback = callbackMap.get(((SenderInfo) msg).getSenderId());
61
+			if (callback != null) {
62
+				callback.onMessageReceived(msg);
63
+			} else {
64
+				Log.w(getClass().getSimpleName(),
65
+						"Nobody is waiting for messages from " + ((SenderInfo) msg).getSenderId());
66
+			}
67
+		} else {
68
+			Log.e(getClass().getSimpleName(),
69
+					"Proxy sent a message that does not contain sender information: " +
70
+							msg.getClass().getSimpleName());
71
+		}
72
+	}
73
+
74
+	public boolean isConnected() {
75
+		return proxyConnection != null &&
76
+				proxyConnection.isOpen();
77
+	}
78
+
79
+	public void close() {
80
+		proxyConnection.close();
81
+	}
82
+
83
+	public void disconnectUser(String id) {
84
+		callbackMap.remove(id);
85
+	}
86
+}

+ 5
- 5
client/src/main/java/fr/insa/clavardator/client/ui/MainController.java View File

@@ -287,12 +287,12 @@ public class MainController implements Initializable {
287 287
 		if (presenceServer != null && online) {
288 288
 			presenceServer.subscribe(
289 289
 					param -> userList.onReceivePresenceNotification(
290
-									param,
291
-									presenceServer.getProxyConnection()),
290
+							param,
291
+							presenceServer.getProxy()),
292 292
 					(e) -> Log.v(
293
-									getClass().getSimpleName(),
294
-									"Error subscribing to presence server",
295
-									e));
293
+							getClass().getSimpleName(),
294
+							"Error subscribing to presence server",
295
+							e));
296 296
 		}
297 297
 	}
298 298
 

+ 52
- 0
client/src/main/java/fr/insa/clavardator/client/users/DirectPeerConnection.java View File

@@ -0,0 +1,52 @@
1
+package fr.insa.clavardator.client.users;
2
+
3
+import fr.insa.clavardator.lib.network.TcpConnection;
4
+import fr.insa.clavardator.lib.util.ErrorCallback;
5
+import fr.insa.clavardator.lib.util.Log;
6
+import fr.insa.clavardator.lib.util.SimpleCallback;
7
+import org.jetbrains.annotations.Nullable;
8
+
9
+import java.io.EOFException;
10
+import java.io.Serializable;
11
+
12
+public class DirectPeerConnection extends PeerConnection {
13
+	private final TcpConnection connection;
14
+
15
+	public DirectPeerConnection(TcpConnection connection, PeerUser user) {
16
+		super(user);
17
+		this.connection = connection;
18
+	}
19
+
20
+	@Override
21
+	protected void send(Serializable message, SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
22
+		connection.send(message, callback, errorCallback);
23
+	}
24
+
25
+
26
+	@Override
27
+	protected void receive(TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback) {
28
+		connection.receive(callback,
29
+				e -> {
30
+					disconnect();
31
+					if (!(e instanceof EOFException)) {
32
+						Log.e(this.getClass().getSimpleName(), "Error receiving message from " + user.getId(), e);
33
+						errorCallback.onError(e);
34
+					}
35
+				});
36
+	}
37
+
38
+
39
+	/**
40
+	 * Close the connection to this user
41
+	 */
42
+	private void closeConnection() {
43
+		if (connection != null && connection.isOpen()) {
44
+			connection.close();
45
+		}
46
+	}
47
+
48
+	@Override
49
+	public void disconnect() {
50
+		closeConnection();
51
+	}
52
+}

+ 34
- 0
client/src/main/java/fr/insa/clavardator/client/users/PeerConnection.java View File

@@ -0,0 +1,34 @@
1
+package fr.insa.clavardator.client.users;
2
+
3
+import fr.insa.clavardator.lib.network.TcpConnection;
4
+import fr.insa.clavardator.lib.util.ErrorCallback;
5
+import fr.insa.clavardator.lib.util.SimpleCallback;
6
+import org.jetbrains.annotations.Nullable;
7
+
8
+import java.io.Serializable;
9
+
10
+public abstract class PeerConnection {
11
+	protected final PeerUser user;
12
+
13
+	protected PeerConnection(PeerUser user) {
14
+		this.user = user;
15
+	}
16
+
17
+	protected abstract void send(Serializable message, SimpleCallback calback, @Nullable ErrorCallback errorCallback);
18
+
19
+	/**
20
+	 * Subscribe to this user messages.
21
+	 * If receiving new user info, update this user.
22
+	 * If receiving text message, store it in the history.
23
+	 *
24
+	 * @param errorCallback Callback on error
25
+	 */
26
+	protected abstract void receive(TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback);
27
+
28
+	/**
29
+	 * Close the connection and set state to disconnected
30
+	 */
31
+	public abstract void disconnect();
32
+
33
+
34
+}

+ 73
- 86
client/src/main/java/fr/insa/clavardator/client/users/PeerUser.java View File

@@ -2,6 +2,7 @@ package fr.insa.clavardator.client.users;
2 2
 
3 3
 import fr.insa.clavardator.client.chat.ChatHistory;
4 4
 import fr.insa.clavardator.client.db.DatabaseController;
5
+import fr.insa.clavardator.client.server.Proxy;
5 6
 import fr.insa.clavardator.lib.errors.UsernameTakenException;
6 7
 import fr.insa.clavardator.lib.message.FileMessage;
7 8
 import fr.insa.clavardator.lib.message.Message;
@@ -14,15 +15,14 @@ import fr.insa.clavardator.lib.util.Log;
14 15
 import org.jetbrains.annotations.NotNull;
15 16
 import org.jetbrains.annotations.Nullable;
16 17
 
17
-import java.io.EOFException;
18 18
 import java.io.File;
19 19
 import java.io.IOException;
20 20
 import java.util.Date;
21 21
 
22 22
 public class PeerUser extends User implements Comparable<PeerUser> {
23
-	protected transient ChatHistory history;
23
+	private final transient ChatHistory history;
24 24
 	private UserState state = UserState.DISCONNECTED;
25
-	private transient TcpConnection connection;
25
+	private PeerConnection connection;
26 26
 
27 27
 	public PeerUser(String id, String username) {
28 28
 		super(id, username);
@@ -34,6 +34,23 @@ public class PeerUser extends User implements Comparable<PeerUser> {
34 34
 		history = new ChatHistory(this);
35 35
 	}
36 36
 
37
+	private void init(String id, String username, ErrorCallback errorCallback) {
38
+		setId(id);
39
+		setUsername(username);
40
+		setState(UserState.CONNECTED);
41
+		connection.receive(msg -> onMessageReceived(msg, errorCallback), errorCallback);
42
+	}
43
+
44
+	public void init(TcpConnection tcpConnection, String id, String username, ErrorCallback errorCallback) {
45
+		connection = new DirectPeerConnection(tcpConnection, this);
46
+		init(id, username, errorCallback);
47
+	}
48
+
49
+	public void init(Proxy proxy, String id, String username, ErrorCallback errorCallback) {
50
+		connection = new ProxyPeerConnection(proxy, this);
51
+		init(id, username, errorCallback);
52
+	}
53
+
37 54
 
38 55
 	/**
39 56
 	 * Sends a basic text message to this user
@@ -44,14 +61,15 @@ public class PeerUser extends User implements Comparable<PeerUser> {
44 61
 	public void sendTextMessage(String msg, @Nullable ErrorCallback errorCallback) {
45 62
 		if (connection != null) {
46 63
 			Log.v(this.getClass().getSimpleName(),
47
-					"Sending message to " + this.getUsername() + " / " + this.getId() + ": " + msg);
64
+					"Sending message to " + getUsername() + " / " + getId() + ": " + msg);
48 65
 			final Message message = new Message(CurrentUser.getInstance(), this, new Date(), msg);
49
-			connection.send(message, () -> history.addMessage(message, errorCallback), errorCallback);
66
+			connection.send(message, () -> addMessageToHistory(message, errorCallback), errorCallback);
50 67
 		} else {
51 68
 			Log.e(this.getClass().getSimpleName(), "Could not send message: connection is not initialized");
52 69
 		}
53 70
 	}
54 71
 
72
+
55 73
 	/**
56 74
 	 * Sends a message containing a file to this user
57 75
 	 *
@@ -65,7 +83,7 @@ public class PeerUser extends User implements Comparable<PeerUser> {
65 83
 			try {
66 84
 				final FileMessage message = new FileMessage(CurrentUser.getInstance(), this, new Date(), msg, file.getPath());
67 85
 				message.storeFile();
68
-				connection.send(message, () -> history.addMessage(message, errorCallback), errorCallback);
86
+				connection.send(message, () -> addMessageToHistory(message, errorCallback), errorCallback);
69 87
 			} catch (IOException e) {
70 88
 				Log.e(this.getClass().getSimpleName(), "Could not send message: error while opening file", e);
71 89
 				if (errorCallback != null) {
@@ -87,103 +105,62 @@ public class PeerUser extends User implements Comparable<PeerUser> {
87 105
 			final String username = CurrentUser.getInstance().getUsername();
88 106
 			Log.v(this.getClass().getSimpleName(),
89 107
 					"Sending current user information to " + this.getUsername() + " / " + this.getId() + ": " + username);
90
-			connection.send(
91
-					new UserInformation(CurrentUser.getInstance()),
92
-					null,
93
-					errorCallback);
108
+			connection.send(new UserInformation(CurrentUser.getInstance()), null, errorCallback);
94 109
 		} else {
95 110
 			Log.e(this.getClass().getSimpleName(), "Could not send new username: connection is not initialized");
96 111
 		}
97 112
 	}
98 113
 
99
-	private void sendUsernameTaken(TcpConnection thisConnection) {
100
-		Log.v(this.getClass().getSimpleName(), "Received username request using current username");
101
-		thisConnection.send(new UsernameTakenException("Username taken", getId()), this::disconnect, null);
114
+	protected void sendUsernameTaken() {
115
+		if (connection != null) {
116
+			Log.v(this.getClass().getSimpleName(), "Received username request using current username");
117
+			UsernameTakenException exception = new UsernameTakenException("Username taken",
118
+					CurrentUser.getInstance().getId(), getId());
119
+			connection.send(exception, connection::disconnect, null);
120
+		} else {
121
+			Log.e(this.getClass().getSimpleName(), "Could not send UsernameTaken: connection is not initialized");
122
+		}
102 123
 	}
103 124
 
104
-	public void init(TcpConnection connection, String id, String username, ErrorCallback errorCallback) {
105
-		this.connection = connection;
106
-		this.setId(id);
107
-		setUsername(username);
108
-		setState(UserState.CONNECTED);
109
-		subscribeToMessages((e) -> {
110
-			disconnect();
111
-			errorCallback.onError(e);
112
-		});
125
+	protected void addMessageToHistory(Message message, @Nullable ErrorCallback errorCallback) {
126
+		history.addMessage(message, errorCallback);
113 127
 	}
114 128
 
115
-	/**
116
-	 * Subscribe to this user messages.
117
-	 * If receiving new user info, update this user.
118
-	 * If receiving text message, store it in the history.
119
-	 *
120
-	 * @param errorCallback Callback on error
121
-	 */
122
-	private void subscribeToMessages(ErrorCallback errorCallback) {
123
-		connection.receive(
124
-				msg -> {
125
-					Log.v(this.getClass().getSimpleName(), "Received message from " + getId());
126
-					if (msg instanceof UserInformation) {
127
-						assert ((UserInformation) msg).id.equals(getId());
128
-						final String receivedUsername = ((UserInformation) msg).getUsername();
129
-						Log.v(this.getClass().getSimpleName(), "Message username: " + receivedUsername);
130
-						if (CurrentUser.getInstance().getUsername().equals(receivedUsername)) {
131
-							sendUsernameTaken(connection);
132
-						} else {
133
-							setUsername(receivedUsername);
134
-						}
135
-					} else if (msg instanceof Message) {
136
-						assert !((Message) msg).getRecipient().id.equals(getId());
137
-
138
-						Log.v(this.getClass().getSimpleName(), "Message text: " + ((Message) msg).getText());
139
-						if (msg instanceof FileMessage) {
140
-							((FileMessage) msg).storeFile();
141
-						}
142
-						history.addMessage((Message) msg, errorCallback);
143
-					} else if (msg instanceof UsernameTakenException) {
144
-						disconnect();
145
-						errorCallback.onError(new Exception("Received username already taken message"));
146
-					}
147
-				},
148
-				e -> {
149
-					if (e instanceof EOFException) {
150
-						disconnect();
151
-					} else {
152
-						Log.e(this.getClass().getSimpleName(), "Error receiving message from " + getId(), e);
153
-						errorCallback.onError(e);
154
-					}
155
-				});
156
-	}
157 129
 
158
-	/**
159
-	 * Close the connection and set state to disconnected
160
-	 */
161
-	public void disconnect() {
162
-		Log.v(this.getClass().getSimpleName(), "Disconnecting from user: " + getId());
163
-		closeConnection();
164
-		setState(UserState.DISCONNECTED);
165
-	}
130
+	protected void onMessageReceived(Object msg, ErrorCallback errorCallback) throws IOException {
131
+		Log.v(this.getClass().getSimpleName(), "Received message from " + getId());
132
+		if (msg instanceof UserInformation) {
133
+			assert ((UserInformation) msg).id.equals(getId());
134
+			final String receivedUsername = ((UserInformation) msg).getUsername();
135
+			Log.v(this.getClass().getSimpleName(), "Message username: " + receivedUsername);
136
+			if (CurrentUser.getInstance().getUsername().equals(receivedUsername)) {
137
+				sendUsernameTaken();
138
+			} else {
139
+				setUsername(receivedUsername);
140
+			}
141
+		} else if (msg instanceof Message) {
142
+			assert !((Message) msg).getRecipient().id.equals(getId());
166 143
 
167
-	@Override
168
-	protected void setUsername(String newUsername) {
169
-		super.setUsername(newUsername);
170
-		final DatabaseController db = new DatabaseController();
171
-		db.updateUsername(new UserInformation(this),
172
-				null,
173
-				e -> Log.e(getClass().getSimpleName(), "Unable to update the username", e));
144
+			Log.v(this.getClass().getSimpleName(), "Message text: " + ((Message) msg).getText());
145
+			if (msg instanceof FileMessage) {
146
+				((FileMessage) msg).storeFile();
147
+			}
148
+			history.addMessage((Message) msg, errorCallback);
149
+		} else if (msg instanceof UsernameTakenException) {
150
+			disconnect();
151
+			errorCallback.onError(new Exception("Received username already taken message"));
152
+		}
174 153
 	}
175 154
 
176
-	/**
177
-	 * Close the connection to this user
178
-	 */
179
-	private void closeConnection() {
180
-		if (connection != null && connection.isOpen()) {
181
-			connection.close();
155
+	public void disconnect() {
156
+		if (connection != null) {
157
+			Log.v(this.getClass().getSimpleName(), "Disconnecting from user: " + getId());
158
+			connection.disconnect();
182 159
 			connection = null;
183 160
 		}
161
+		setState(UserState.DISCONNECTED);
184 162
 	}
185 163
 
186
-
187 164
 	/**
188 165
 	 * Gets the value of history
189 166
 	 *
@@ -203,6 +180,16 @@ public class PeerUser extends User implements Comparable<PeerUser> {
203 180
 		this.state = state;
204 181
 	}
205 182
 
183
+	@Override
184
+	protected void setUsername(String newUsername) {
185
+		super.setUsername(newUsername);
186
+		final DatabaseController db = new DatabaseController();
187
+		db.updateUsername(new UserInformation(this),
188
+				null,
189
+				e -> Log.e(getClass().getSimpleName(), "Unable to update the username", e));
190
+	}
191
+
192
+
206 193
 	/**
207 194
 	 * Check if this user is active.
208 195
 	 *

+ 43
- 0
client/src/main/java/fr/insa/clavardator/client/users/ProxyPeerConnection.java View File

@@ -0,0 +1,43 @@
1
+package fr.insa.clavardator.client.users;
2
+
3
+import fr.insa.clavardator.client.server.Proxy;
4
+import fr.insa.clavardator.lib.network.TcpConnection;
5
+import fr.insa.clavardator.lib.util.ErrorCallback;
6
+import fr.insa.clavardator.lib.util.Log;
7
+import fr.insa.clavardator.lib.util.SimpleCallback;
8
+import org.jetbrains.annotations.Nullable;
9
+
10
+import java.io.EOFException;
11
+import java.io.Serializable;
12
+
13
+public class ProxyPeerConnection extends PeerConnection {
14
+	private final Proxy proxy;
15
+
16
+	public ProxyPeerConnection(Proxy proxy, PeerUser user) {
17
+		super(user);
18
+		this.proxy = proxy;
19
+	}
20
+
21
+	@Override
22
+	protected void send(Serializable message, SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
23
+		proxy.send(message, callback, errorCallback);
24
+	}
25
+
26
+
27
+	@Override
28
+	protected void receive(TcpConnection.MessageReceivedCallback callback, ErrorCallback errorCallback) {
29
+		proxy.receive(user.getId(), callback,
30
+				e -> {
31
+					disconnect();
32
+					if (!(e instanceof EOFException)) {
33
+						Log.e(this.getClass().getSimpleName(), "Error receiving message from " + user.getId(), e);
34
+						errorCallback.onError(e);
35
+					}
36
+				});
37
+	}
38
+
39
+	@Override
40
+	public void disconnect() {
41
+		proxy.disconnectUser(user.getId());
42
+	}
43
+}

+ 4
- 4
client/src/main/java/fr/insa/clavardator/client/users/UserList.java View File

@@ -3,7 +3,7 @@ package fr.insa.clavardator.client.users;
3 3
 import fr.insa.clavardator.client.db.DatabaseController;
4 4
 import fr.insa.clavardator.client.network.NetDiscoverer;
5 5
 import fr.insa.clavardator.client.network.PeerHandshake;
6
-import fr.insa.clavardator.lib.network.TcpConnection;
6
+import fr.insa.clavardator.client.server.Proxy;
7 7
 import fr.insa.clavardator.lib.network.TcpListener;
8 8
 import fr.insa.clavardator.lib.users.User;
9 9
 import fr.insa.clavardator.lib.users.UserInformation;
@@ -47,7 +47,7 @@ public class UserList {
47 47
 		}, errorCallback);
48 48
 	}
49 49
 
50
-	public void onReceivePresenceNotification(ArrayList<UserInformation> newPresenceUsers, TcpConnection proxyConnection) {
50
+	public void onReceivePresenceNotification(ArrayList<UserInformation> newPresenceUsers, Proxy proxy) {
51 51
 		newPresenceUsers.forEach((userInfo -> {
52 52
 			final PeerUser savedUser = userHashmap.get(userInfo.id);
53 53
 			if (savedUser != null) {
@@ -55,12 +55,12 @@ public class UserList {
55 55
 					Log.v(getClass().getSimpleName(), "Received user from presence server already known and connected");
56 56
 				} else {
57 57
 					Log.v(getClass().getSimpleName(), "Received user from presence server already known but not connected, connecting...");
58
-					savedUser.init(proxyConnection, userInfo.id, userInfo.getUsername(), null);
58
+					savedUser.init(proxy, userInfo.id, userInfo.getUsername(), null);
59 59
 				}
60 60
 			} else {
61 61
 				Log.v(getClass().getSimpleName(), "Received new user from presence server");
62 62
 				final PeerUser user = new PeerUser();
63
-				user.init(proxyConnection, userInfo.id, userInfo.getUsername(), null);
63
+				user.init(proxy, userInfo.id, userInfo.getUsername(), null);
64 64
 				userHashmap.put(user.getId(), user);
65 65
 				Platform.runLater(() -> userObservableList.add(user));
66 66
 			}

+ 17
- 2
lib/src/main/java/fr/insa/clavardator/lib/errors/UsernameTakenException.java View File

@@ -1,12 +1,27 @@
1 1
 package fr.insa.clavardator.lib.errors;
2 2
 
3
+import fr.insa.clavardator.lib.message.RecipientInfo;
4
+import fr.insa.clavardator.lib.message.SenderInfo;
5
+
3 6
 import java.io.Serializable;
4 7
 
5
-public class UsernameTakenException extends Exception implements Serializable {
8
+public class UsernameTakenException extends Exception implements Serializable, RecipientInfo, SenderInfo {
6 9
 	public final String recipient;
10
+	public final String sender;
7 11
 
8
-	public UsernameTakenException(String message, String recipient) {
12
+	public UsernameTakenException(String message, String sender, String recipient) {
9 13
 		super(message);
14
+		this.sender = sender;
10 15
 		this.recipient = recipient;
11 16
 	}
17
+
18
+	@Override
19
+	public String getRecipientId() {
20
+		return recipient;
21
+	}
22
+
23
+	@Override
24
+	public String getSenderId() {
25
+		return sender;
26
+	}
12 27
 }

+ 3
- 2
lib/src/main/java/fr/insa/clavardator/lib/message/FileMessage.java View File

@@ -86,8 +86,9 @@ public class FileMessage extends Message {
86 86
 
87 87
 		// write to the file
88 88
 		if (rawFile == null) {
89
-			FileInputStream stream = new FileInputStream(fileName);
90
-			rawFile = stream.readAllBytes();
89
+			FileInputStream istream = new FileInputStream(fileName);
90
+			rawFile = istream.readAllBytes();
91
+			istream.close();
91 92
 		}
92 93
 
93 94
 		FileOutputStream ostream = new FileOutputStream(file);

+ 11
- 1
lib/src/main/java/fr/insa/clavardator/lib/message/Message.java View File

@@ -6,7 +6,7 @@ import fr.insa.clavardator.lib.users.UserInformation;
6 6
 import java.io.Serializable;
7 7
 import java.util.Date;
8 8
 
9
-public class Message implements Serializable {
9
+public class Message implements Serializable, SenderInfo, RecipientInfo {
10 10
 	private final String text;
11 11
 	private final Date date;
12 12
 	private final UserInformation sender;
@@ -59,4 +59,14 @@ public class Message implements Serializable {
59 59
 				", recipient=" + recipient +
60 60
 				'}';
61 61
 	}
62
+
63
+	@Override
64
+	public String getSenderId() {
65
+		return sender.id;
66
+	}
67
+
68
+	@Override
69
+	public String getRecipientId() {
70
+		return recipient.id;
71
+	}
62 72
 }

+ 5
- 0
lib/src/main/java/fr/insa/clavardator/lib/message/RecipientInfo.java View File

@@ -0,0 +1,5 @@
1
+package fr.insa.clavardator.lib.message;
2
+
3
+public interface RecipientInfo {
4
+	String getRecipientId();
5
+}

+ 5
- 0
lib/src/main/java/fr/insa/clavardator/lib/message/SenderInfo.java View File

@@ -0,0 +1,5 @@
1
+package fr.insa.clavardator.lib.message;
2
+
3
+public interface SenderInfo {
4
+	String getSenderId();
5
+}

+ 18
- 16
lib/src/main/java/fr/insa/clavardator/lib/network/TcpConnection.java View File

@@ -1,6 +1,7 @@
1 1
 package fr.insa.clavardator.lib.network;
2 2
 
3 3
 import fr.insa.clavardator.lib.util.ErrorCallback;
4
+import fr.insa.clavardator.lib.util.SimpleCallback;
4 5
 import org.jetbrains.annotations.NotNull;
5 6
 import org.jetbrains.annotations.Nullable;
6 7
 
@@ -19,6 +20,7 @@ public class TcpConnection {
19 20
 	private ObjectInputStream inputStream;
20 21
 	private boolean shouldStop = false;
21 22
 	private final int port;
23
+	private final Object outputStreamGuard = new Object();
22 24
 
23 25
 	/**
24 26
 	 * Creates a new connection, and connects to the peer
@@ -61,7 +63,7 @@ public class TcpConnection {
61 63
 	 * @param callback      The function to call on success
62 64
 	 * @param errorCallback The function to call on error
63 65
 	 */
64
-	public void send(Serializable message, @Nullable MessageSentCallback callback, @Nullable ErrorCallback errorCallback) {
66
+	public void send(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
65 67
 		Sender sender = new Sender(message, callback, errorCallback);
66 68
 		sender.start();
67 69
 	}
@@ -93,8 +95,10 @@ public class TcpConnection {
93 95
 	 */
94 96
 	public void close() {
95 97
 		shouldStop = true;
96
-		if (socket != null) {
98
+		if (outputStream != null) {
97 99
 			try {
100
+				outputStream.close();
101
+				inputStream.close();
98 102
 				socket.close();
99 103
 			} catch (IOException ignored) {
100 104
 			}
@@ -103,7 +107,8 @@ public class TcpConnection {
103 107
 
104 108
 	/**
105 109
 	 * Checks if the current connection is open
106
-	 * @return	True if the socket is still open
110
+	 *
111
+	 * @return True if the socket is still open
107 112
 	 */
108 113
 	public boolean isOpen() {
109 114
 		return socket != null && socket.isConnected() && !socket.isClosed();
@@ -138,31 +143,33 @@ public class TcpConnection {
138 143
 
139 144
 	private class Sender extends Thread {
140 145
 		private final Serializable message;
141
-		private final MessageSentCallback callback;
146
+		private final SimpleCallback callback;
142 147
 		private final ErrorCallback errorCallback;
143 148
 
144 149
 		/**
145 150
 		 * Constructs a thread that sends a message using the socket of the outer class
146 151
 		 *
147
-		 * @param message      The message to send
152
+		 * @param message       The message to send
148 153
 		 * @param callback      The function to call on success
149 154
 		 * @param errorCallback The function to call on error
150 155
 		 */
151
-		public Sender(Serializable message, @Nullable MessageSentCallback callback, @Nullable ErrorCallback errorCallback) {
156
+		public Sender(Serializable message, @Nullable SimpleCallback callback, @Nullable ErrorCallback errorCallback) {
152 157
 			this.message = message;
153 158
 			this.callback = callback;
154 159
 			this.errorCallback = errorCallback;
155 160
 		}
156 161
 
157 162
 		@Override
158
-		synchronized public void run() {
163
+		public void run() {
159 164
 			try {
160
-				if (outputStream == null) {
161
-					outputStream = new ObjectOutputStream(socket.getOutputStream());
165
+				synchronized (outputStreamGuard) {
166
+					if (outputStream == null) {
167
+						outputStream = new ObjectOutputStream(socket.getOutputStream());
168
+					}
169
+					outputStream.writeObject(message);
162 170
 				}
163
-				outputStream.writeObject(message);
164 171
 				if (callback != null)
165
-					callback.onMessageSent();
172
+					callback.call();
166 173
 			} catch (IOException e) {
167 174
 				if (errorCallback != null && !shouldStop)
168 175
 					errorCallback.onError(e);
@@ -211,9 +218,4 @@ public class TcpConnection {
211 218
 	public interface MessageReceivedCallback {
212 219
 		void onMessageReceived(Object msg) throws IOException;
213 220
 	}
214
-
215
-	public interface MessageSentCallback {
216
-		void onMessageSent();
217
-	}
218
-
219 221
 }

+ 2
- 2
lib/src/main/java/fr/insa/clavardator/lib/network/TcpListener.java View File

@@ -49,7 +49,7 @@ public class TcpListener {
49 49
 		private final NewConnectionCallback callback;
50 50
 		private final ErrorCallback errorCallback;
51 51
 		private ServerSocket server;
52
-		private int port;
52
+		private final int port;
53 53
 
54 54
 		public Acceptor(int port, NewConnectionCallback callback, ErrorCallback errorCallback) {
55 55
 			this.port = port;
@@ -69,11 +69,11 @@ public class TcpListener {
69 69
 				if (!shouldStop)
70 70
 					errorCallback.onError(e);
71 71
 			}
72
-
73 72
 		}
74 73
 
75 74
 		public void stopAccepting() {
76 75
 			shouldStop = true;
76
+//			TODO: call interrupt() with ChannelServerSocket instead of closing the socket?
77 77
 			try {
78 78
 				server.close();
79 79
 			} catch (IOException ignored) {

+ 8
- 1
lib/src/main/java/fr/insa/clavardator/lib/users/UserInformation.java View File

@@ -1,11 +1,13 @@
1 1
 package fr.insa.clavardator.lib.users;
2 2
 
3
+import fr.insa.clavardator.lib.message.SenderInfo;
4
+
3 5
 import java.io.Serializable;
4 6
 
5 7
 /**
6 8
  * Class used to serialize useful user information
7 9
  */
8
-public class UserInformation implements Serializable {
10
+public class UserInformation implements Serializable, SenderInfo {
9 11
 	public final String id;
10 12
 	private final String username;
11 13
 	private final UserState state;
@@ -46,4 +48,9 @@ public class UserInformation implements Serializable {
46 48
 	public String toString() {
47 49
 		return "UserInfo " + id + '(' + username + ')';
48 50
 	}
51
+
52
+	@Override
53
+	public String getSenderId() {
54
+		return id;
55
+	}
49 56
 }

+ 19
- 22
server/src/main/java/fr/insa/clavardator/server/Proxy.java View File

@@ -1,7 +1,6 @@
1 1
 package fr.insa.clavardator.server;
2 2
 
3
-import fr.insa.clavardator.lib.errors.UsernameTakenException;
4
-import fr.insa.clavardator.lib.message.Message;
3
+import fr.insa.clavardator.lib.message.RecipientInfo;
5 4
 import fr.insa.clavardator.lib.network.TcpConnection;
6 5
 import fr.insa.clavardator.lib.network.TcpListener;
7 6
 import fr.insa.clavardator.lib.users.UserInformation;
@@ -24,17 +23,9 @@ public class Proxy {
24 23
 					Log.v(getClass().getSimpleName(), "Accepting a new user");
25 24
 					TcpConnection connection = new TcpConnection(clientSocket);
26 25
 					connection.receive(msg -> {
27
-
28
-						if (msg instanceof Message) {
29
-							Log.v(getClass().getSimpleName(), "Transmitting message: " + msg);
30
-							transmitMessage((Serializable) msg, ((Message) msg).getRecipient().id);
31
-
32
-						} else if (msg instanceof UsernameTakenException) {
33
-							UsernameTakenException unameTaken = ((UsernameTakenException) msg);
34
-							transmitMessage(unameTaken, unameTaken.recipient);
35
-
36
-						} else if (msg instanceof UserInformation) {
37
-							Log.v(getClass().getSimpleName(), "Registering new user: " + msg);
26
+						if (msg instanceof UserInformation) {
27
+							// Send UserInformation to all other connected users
28
+							Log.v(getClass().getSimpleName(), "Received UserInformation: " + msg);
38 29
 							users.put(((UserInformation) msg).id, connection);
39 30
 							for (String userId : users.keySet()) {
40 31
 								UserInformation userInfo = ((UserInformation) msg);
@@ -42,20 +33,25 @@ public class Proxy {
42 33
 									transmitMessage((Serializable) msg, userId);
43 34
 								}
44 35
 							}
36
+
37
+						} else if (msg instanceof RecipientInfo) {
38
+							// Send other messages only to the recipient
39
+							transmitMessage((Serializable) msg, ((RecipientInfo) msg).getRecipientId());
40
+						} else {
41
+							Log.e(getClass().getSimpleName(), "Unexpected message received: " + msg.toString());
45 42
 						}
46 43
 
47 44
 					}, e -> {
48
-						if (e instanceof EOFException) {
49
-							for (Map.Entry<String, TcpConnection> user : users.entrySet()) {
50
-								if (user.getValue().equals(connection)) {
51
-									Log.v(getClass().getSimpleName(), "Disconnecting user " + user.getKey());
52
-									users.remove(user.getKey());
53
-									break;
54
-								}
55
-							}
56
-						} else {
45
+						if (!(e instanceof EOFException)) {
57 46
 							Log.e(getClass().getSimpleName(), "Error while receiving message to transmit", e);
58 47
 						}
48
+						for (Map.Entry<String, TcpConnection> user : users.entrySet()) {
49
+							if (user.getValue().equals(connection)) {
50
+								Log.v(getClass().getSimpleName(), "Disconnecting user " + user.getKey());
51
+								users.remove(user.getKey());
52
+								break;
53
+							}
54
+						}
59 55
 					});
60 56
 				},
61 57
 				e -> Log.e(getClass().getSimpleName(), "Error while accepting a user", e));
@@ -67,6 +63,7 @@ public class Proxy {
67 63
 		if (user == null) {
68 64
 			Log.e(getClass().getSimpleName(), "Cannot find the recipient in the connected users");
69 65
 		} else {
66
+			Log.v(getClass().getSimpleName(), "Transmitting message: " + msg);
70 67
 			user.send(msg, null, e -> Log.e(getClass().getSimpleName(), "Error while sending message", e));
71 68
 		}
72 69
 	}

Loading…
Cancel
Save