Merge remote-tracking branch 'origin/master'

This commit is contained in:
Arnaud Vergnet 2020-12-02 12:16:39 +01:00
commit 725c809436
8 changed files with 163 additions and 82 deletions

View file

@ -2,7 +2,6 @@ package fr.insa.clavardator;
import fr.insa.clavardator.network.NetDiscoverer; import fr.insa.clavardator.network.NetDiscoverer;
import fr.insa.clavardator.ui.MainController; import fr.insa.clavardator.ui.MainController;
import fr.insa.clavardator.util.ErrorHandler;
import javafx.application.Application; import javafx.application.Application;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Parent; import javafx.scene.Parent;
@ -36,12 +35,10 @@ public class MainApp extends Application {
stage.show(); stage.show();
// Network discovery test // Network discovery test
ErrorHandler.getInstance().addObserver(evt -> ((Exception)evt.getNewValue()).printStackTrace());
NetDiscoverer.discoverActiveUsers("Broadcast", NetDiscoverer.discoverActiveUsers("Broadcast",
(ipAddr, data) -> System.out.println("User detected at address : " + ipAddr.toString()) (ipAddr, data) -> System.out.println("User detected at address : " + ipAddr.toString()),
); Throwable::printStackTrace);
NetDiscoverer.startDiscoveryListening("Yohan", null); NetDiscoverer.startDiscoveryListening("Yohan", null, Throwable::printStackTrace);
} }
@Override @Override

View file

@ -1,6 +1,6 @@
package fr.insa.clavardator.network; package fr.insa.clavardator.network;
import fr.insa.clavardator.util.ErrorHandler; import fr.insa.clavardator.util.ErrorCallback;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.IOException; import java.io.IOException;
@ -26,28 +26,41 @@ public class NetDiscoverer {
/** /**
* Discovers all active users in the network, and call the callback for each of them * Discovers all active users in the network, and call the callback for each of them
* *
* @param callback function to call when a new user is discovered * @param broadcastMessage The message to send as broadcast
* @param callback function to call when a new user is discovered
* @param errorCallback function to call on error
*/ */
public static void discoverActiveUsers(String broadcastMessage, ResponseReceivedCallback callback) { public static void discoverActiveUsers(String broadcastMessage, ResponseReceivedCallback callback, ErrorCallback errorCallback) {
ResponseListener receiver = new ResponseListener(callback); ResponseListener receiver = new ResponseListener(callback, errorCallback);
BroadcastSender sender = new BroadcastSender(broadcastMessage); BroadcastSender sender = new BroadcastSender(broadcastMessage, errorCallback);
receiver.start(); receiver.start();
sender.start(); sender.start();
} }
/** /**
* Starts to listen for discovery broadcasts and answers them * Starts to listen for discovery broadcasts and answers them
*
* @param responseMessage The message to send in response to broadcasts
* @param onBroadcastReceived The function to call on success
* @param errorCallback The function to call on error
*/ */
public static void startDiscoveryListening(String responseMessage, @Nullable BroadcastReceivedCallback onBroadcastReceived) { public static void startDiscoveryListening(String responseMessage, @Nullable BroadcastReceivedCallback onBroadcastReceived, @Nullable ErrorCallback errorCallback) {
BroadcastListener listener = new BroadcastListener((ipAddr, data) -> { BroadcastListener listener = new BroadcastListener((ipAddr, data) -> {
if (onBroadcastReceived != null) if (onBroadcastReceived != null)
onBroadcastReceived.onBroadcastReceived(ipAddr, data); onBroadcastReceived.onBroadcastReceived(ipAddr, data);
new ResponseSender(ipAddr, responseMessage).start(); ResponseSender sender = new ResponseSender(ipAddr, responseMessage, errorCallback);
} sender.start();
); }, e -> {
if (errorCallback != null)
errorCallback.onError(e);
});
listener.start(); listener.start();
} }
/**
* Stop network discovery and listening
*/
public static void stopDiscovery() { public static void stopDiscovery() {
shouldStop = true; shouldStop = true;
if (broadcastListener != null) if (broadcastListener != null)
@ -58,15 +71,18 @@ public class NetDiscoverer {
private static class BroadcastSender extends Thread { private static class BroadcastSender extends Thread {
String broadcastMessage; private final String broadcastMessage;
private final ErrorCallback errorCallback;
/** /**
* Constructs and starts a thread that sends a broadcast over the network * Constructs a thread that sends a broadcast over the network
* *
* @param broadcastMessage The message to send * @param broadcastMessage The message to send
* @param errorCallback The function to call on error
*/ */
public BroadcastSender(String broadcastMessage) { public BroadcastSender(String broadcastMessage, ErrorCallback errorCallback) {
this.broadcastMessage = broadcastMessage; this.broadcastMessage = broadcastMessage;
this.errorCallback = errorCallback;
} }
@Override @Override
@ -81,22 +97,30 @@ public class NetDiscoverer {
} }
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.getInstance().notifyError(e); errorCallback.onError(e);
} }
} }
} }
private static class BroadcastListener extends Thread { private static class BroadcastListener extends Thread {
BroadcastReceivedCallback callback; private final BroadcastReceivedCallback callback;
private final ErrorCallback errorCallback;
public BroadcastListener(BroadcastReceivedCallback callback) { /**
* Constructs a thread that sends a broadcast over the network, using all available interfaces
*
* @param callback The function to call on success
* @param errorCallback The function to call on error
*/
public BroadcastListener(BroadcastReceivedCallback callback, ErrorCallback errorCallback) {
this.callback = callback; this.callback = callback;
this.errorCallback = errorCallback;
} }
@Override @Override
public void run() { public void run() {
while (!shouldStop) { try {
try { while (!shouldStop) {
broadcastListener = new DatagramSocket(null); broadcastListener = new DatagramSocket(null);
broadcastListener.setOption(StandardSocketOptions.SO_REUSEPORT, true); broadcastListener.setOption(StandardSocketOptions.SO_REUSEPORT, true);
broadcastListener.setOption(StandardSocketOptions.SO_REUSEADDR, true); broadcastListener.setOption(StandardSocketOptions.SO_REUSEADDR, true);
@ -108,27 +132,31 @@ public class NetDiscoverer {
broadcastListener.receive(receivedPacket); broadcastListener.receive(receivedPacket);
// System.out.println("broadcast received from " + receivedPacket.getAddress().toString()); // System.out.println("broadcast received from " + receivedPacket.getAddress().toString());
callback.onBroadcastReceived(receivedPacket.getAddress(), new String(receivedPacket.getData())); callback.onBroadcastReceived(receivedPacket.getAddress(), new String(receivedPacket.getData()));
} catch (IOException e) { }
if (!shouldStop) { } catch (IOException e) {
ErrorHandler.getInstance().notifyError(e); if (!shouldStop) {
} errorCallback.onError(e);
} }
} }
} }
} }
private static class ResponseSender extends Thread { private static class ResponseSender extends Thread {
String message; private final String message;
InetAddress address; private final InetAddress address;
private final ErrorCallback errorCallback;
/** /**
* Constructs and starts a thread that sends a broadcast over the network * Constructs a thread that sends a UDP response
* *
* @param message The message to send * @param address The address of the remote host
* @param message The message to send
* @param errorCallback The function to call on error
*/ */
public ResponseSender(InetAddress address, String message) { public ResponseSender(InetAddress address, String message, ErrorCallback errorCallback) {
this.address = address; this.address = address;
this.message = message; this.message = message;
this.errorCallback = errorCallback;
} }
@Override @Override
@ -139,22 +167,30 @@ public class NetDiscoverer {
responseSocket.send(new DatagramPacket(buf, buf.length, address, RESPONSE_PORT)); responseSocket.send(new DatagramPacket(buf, buf.length, address, RESPONSE_PORT));
// System.out.println("Response sent to " + address.toString()); // System.out.println("Response sent to " + address.toString());
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.getInstance().notifyError(e); errorCallback.onError(e);
} }
} }
} }
private static class ResponseListener extends Thread { private static class ResponseListener extends Thread {
ResponseReceivedCallback callback; private final ResponseReceivedCallback callback;
private final ErrorCallback errorCallback;
public ResponseListener(ResponseReceivedCallback callback) { /**
* Constructs a thread that receives all UDP responses, until stopDiscovery() is called
*
* @param callback The function to call on success
* @param errorCallback The function to call on error
*/
public ResponseListener(ResponseReceivedCallback callback, ErrorCallback errorCallback) {
this.callback = callback; this.callback = callback;
this.errorCallback = errorCallback;
} }
@Override @Override
public void run() { public void run() {
while (!shouldStop) { try {
try { while (!shouldStop) {
responseListener = new DatagramSocket(null); responseListener = new DatagramSocket(null);
responseListener.setOption(StandardSocketOptions.SO_REUSEPORT, true); responseListener.setOption(StandardSocketOptions.SO_REUSEPORT, true);
responseListener.setOption(StandardSocketOptions.SO_REUSEADDR, true); responseListener.setOption(StandardSocketOptions.SO_REUSEADDR, true);
@ -165,10 +201,10 @@ public class NetDiscoverer {
responseListener.receive(receivedPacket); responseListener.receive(receivedPacket);
// System.out.println("response received from " + receivedPacket.getAddress().toString()); // System.out.println("response received from " + receivedPacket.getAddress().toString());
callback.onResponseReceived(receivedPacket.getAddress(), new String(receivedPacket.getData())); callback.onResponseReceived(receivedPacket.getAddress(), new String(receivedPacket.getData()));
} catch (IOException e) { }
if (!shouldStop) { } catch (IOException e) {
ErrorHandler.getInstance().notifyError(e); if (!shouldStop) {
} errorCallback.onError(e);
} }
} }
} }
@ -177,7 +213,7 @@ public class NetDiscoverer {
static List<InetAddress> listAllBroadcastAddresses() throws SocketException { static List<InetAddress> listAllBroadcastAddresses() throws SocketException {
List<InetAddress> broadcastList = new ArrayList<>(); List<InetAddress> broadcastList = new ArrayList<>();
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) { while (interfaces.hasMoreElements()) {
NetworkInterface networkInterface = interfaces.nextElement(); NetworkInterface networkInterface = interfaces.nextElement();
@ -192,12 +228,11 @@ public class NetDiscoverer {
} }
public interface ResponseReceivedCallback {
void onResponseReceived(InetAddress ipAddr, String data);
}
private interface BroadcastReceivedCallback { private interface BroadcastReceivedCallback {
void onBroadcastReceived(InetAddress ipAddr, String data); void onBroadcastReceived(InetAddress ipAddr, String data);
} }
public interface ResponseReceivedCallback {
void onResponseReceived(InetAddress ipAddr, String data);
}
} }

View file

@ -1,9 +1,12 @@
package fr.insa.clavardator.network; package fr.insa.clavardator.network;
import fr.insa.clavardator.chat.Message; import fr.insa.clavardator.chat.Message;
import fr.insa.clavardator.util.ErrorHandler; import fr.insa.clavardator.util.ErrorCallback;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.net.InetAddress; import java.net.InetAddress;
@ -12,76 +15,107 @@ import java.net.Socket;
public class PeerConnection { public class PeerConnection {
public static final short PORT = 31598; public static final short PORT = 31598;
private Socket socket; private final ObjectOutputStream outputStream;
private final ObjectInputStream inputStream;
public PeerConnection(InetAddress ipAddr) { public PeerConnection(InetAddress ipAddr) throws IOException {
try { Socket socket = new Socket(ipAddr, PORT);
socket = new Socket(ipAddr, PORT); outputStream = new ObjectOutputStream(socket.getOutputStream());
} catch (IOException e) { inputStream = new ObjectInputStream(socket.getInputStream());
ErrorHandler.getInstance().notifyError(e);
}
} }
/** /**
* @param message * Sends a message to the peer
* @param callback *
* @param message The message to send
* @param callback The function to call on success
* @param errorCallback The function to call on error
*/ */
public void sendMessage(Message message, MessageSentCallback callback) { public void sendMessage(Message message, MessageSentCallback callback, ErrorCallback errorCallback) {
Sender sender = new Sender(message, callback); Sender sender = new Sender(message, callback, errorCallback);
sender.start(); sender.start();
} }
public void receiveMessage(MessageReceivedCallback callback) { /**
Receiver receiver = new Receiver(callback); * Receives a message from the peer
*
* @param callback The function to call on success
* @param errorCallback The function to call on error
*/
public void receiveMessage(MessageReceivedCallback callback, ErrorCallback errorCallback) {
Receiver receiver = new Receiver(callback, errorCallback);
receiver.start(); receiver.start();
} }
private class Sender extends Thread { private class Sender extends Thread {
Serializable message; private final Serializable message;
MessageSentCallback callback; private final MessageSentCallback callback;
private final ErrorCallback errorCallback;
/** /**
* Constructs and starts a thread that sends a message using the socket of the outer class * Constructs a thread that sends a message using the socket of the outer class
* @param messsage The message to send *
* @param callback The function to call once finished * @param messsage The message to send
* @param callback The function to call on success
* @param errorCallback The function to call on error
*/ */
public Sender(Serializable messsage, MessageSentCallback callback) { public Sender(Serializable messsage, @Nullable MessageSentCallback callback, @Nullable ErrorCallback errorCallback) {
this.message = messsage; this.message = messsage;
this.callback = callback; this.callback = callback;
this.errorCallback = errorCallback;
} }
@Override @Override
synchronized public void run() { synchronized public void run() {
try { try {
// TODO: store the oos in Peer connection? outputStream.writeObject(message);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); if (callback != null)
oos.writeObject(message); callback.onMessageSent();
} catch (IOException e) { } catch (IOException e) {
ErrorHandler.getInstance().notifyError(e); if (errorCallback != null)
errorCallback.onError(e);
} }
callback.onMessageSent();
} }
} }
private class Receiver extends Thread { private class Receiver extends Thread {
MessageReceivedCallback callback; private final MessageReceivedCallback callback;
private final ErrorCallback errorCallback;
public Receiver(MessageReceivedCallback callback) { /**
* Constructs a thread that receives a message using the socket of the outer class
*
* @param callback The function to call on success
* @param errorCallback The function to call on error
*/
public Receiver(@NotNull MessageReceivedCallback callback, @Nullable ErrorCallback errorCallback) {
this.callback = callback; this.callback = callback;
this.errorCallback = errorCallback;
} }
@Override @Override
public void run() { public void run() {
//TODO try {
// callback.onMessageReceived(); Object msg = inputStream.readObject();
if (msg.getClass().isInstance(Message.class)) {
callback.onMessageReceived((Message) msg);
} else {
if (errorCallback != null)
errorCallback.onError(new ClassCastException("The message received is not a valid Message object"));
}
} catch (IOException | ClassNotFoundException e) {
if (errorCallback != null)
errorCallback.onError(e);
}
} }
} }
public interface MessageReceivedCallback { public interface MessageReceivedCallback {
void onMessageReceived(Message msg); void onMessageReceived(Message msg);
} }
public interface MessageSentCallback { public interface MessageSentCallback {
void onMessageSent(); void onMessageSent();
} }

View file

@ -11,9 +11,9 @@ import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.URL; import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@ -65,7 +65,7 @@ public class UserListController implements Initializable {
new PeerUser("Dodo1"), new PeerUser("Dodo1"),
new ActiveUser("Coucou2", InetAddress.getLocalHost()) new ActiveUser("Coucou2", InetAddress.getLocalHost())
); );
} catch (UnknownHostException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
if (activeList != null) { if (activeList != null) {

View file

@ -3,6 +3,7 @@ package fr.insa.clavardator.users;
import fr.insa.clavardator.chat.Message; import fr.insa.clavardator.chat.Message;
import fr.insa.clavardator.network.PeerConnection; import fr.insa.clavardator.network.PeerConnection;
import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
@ -10,7 +11,7 @@ public class ActiveUser extends PeerUser {
private transient PeerConnection connection; private transient PeerConnection connection;
public ActiveUser(String username, InetAddress ipAddr) { public ActiveUser(String username, InetAddress ipAddr) throws IOException {
super(username); super(username);
connection = new PeerConnection(ipAddr); connection = new PeerConnection(ipAddr);
} }
@ -19,6 +20,7 @@ public class ActiveUser extends PeerUser {
* @param message * @param message
*/ */
public void sendMessage(Message message) { public void sendMessage(Message message) {
// connection.sendMessage(message);
} }

View file

@ -2,9 +2,11 @@ package fr.insa.clavardator.users;
import fr.insa.clavardator.db.DatabaseController; import fr.insa.clavardator.db.DatabaseController;
import fr.insa.clavardator.network.NetDiscoverer; import fr.insa.clavardator.network.NetDiscoverer;
import fr.insa.clavardator.util.ErrorCallback;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -47,12 +49,17 @@ public class UserList {
/** /**
* *
*/ */
public void discoverActiveUsers() { public void discoverActiveUsers(ErrorCallback errorCallback) {
NetDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> { NetDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> {
ActiveUser newUser = new ActiveUser("", ipAddr); // TODO find username ActiveUser newUser = null;
try {
newUser = new ActiveUser("", ipAddr); // TODO find username
} catch (IOException e) {
errorCallback.onError(e);
}
activeUsers.add(newUser); activeUsers.add(newUser);
pcs.firePropertyChange("activeUsers", null, newUser); pcs.firePropertyChange("activeUsers", null, newUser);
}); }, errorCallback);
} }
} }

View file

@ -0,0 +1,5 @@
package fr.insa.clavardator.util;
public interface ErrorCallback {
void onError(Exception e);
}

View file

@ -3,6 +3,7 @@ package fr.insa.clavardator.util;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
@Deprecated
public class ErrorHandler { public class ErrorHandler {
private ErrorHandler() {} private ErrorHandler() {}