2020-02-04 19:46:18 +01:00
|
|
|
package envoy.client.net;
|
2019-10-06 10:45:19 +02:00
|
|
|
|
2020-09-08 20:41:01 +02:00
|
|
|
import java.io.*;
|
2019-12-29 11:54:05 +01:00
|
|
|
import java.net.Socket;
|
2020-04-10 21:57:05 +02:00
|
|
|
import java.util.concurrent.TimeoutException;
|
2020-09-08 20:41:01 +02:00
|
|
|
import java.util.logging.*;
|
2019-11-27 17:07:25 +01:00
|
|
|
|
2020-10-19 18:17:51 +02:00
|
|
|
import dev.kske.eventbus.*;
|
|
|
|
import dev.kske.eventbus.Event;
|
|
|
|
|
2020-01-02 16:11:41 +01:00
|
|
|
import envoy.data.*;
|
2020-02-07 15:27:26 +01:00
|
|
|
import envoy.event.*;
|
2020-09-08 20:41:01 +02:00
|
|
|
import envoy.util.*;
|
|
|
|
|
2020-11-30 22:19:45 +01:00
|
|
|
import envoy.client.data.ClientConfig;
|
2020-10-19 18:17:51 +02:00
|
|
|
import envoy.client.event.EnvoyCloseEvent;
|
2019-10-06 10:45:19 +02:00
|
|
|
|
|
|
|
/**
|
2020-10-19 18:17:51 +02:00
|
|
|
* Establishes a connection to the server, performs a handshake and delivers certain objects to the
|
|
|
|
* server.
|
2019-12-21 18:29:59 +01:00
|
|
|
*
|
2019-10-12 08:19:00 +02:00
|
|
|
* @author Kai S. K. Engelbart
|
|
|
|
* @author Maximilian Käfer
|
2019-10-12 14:45:58 +02:00
|
|
|
* @author Leon Hofmeister
|
2020-03-23 21:52:33 +01:00
|
|
|
* @since Envoy Client v0.1-alpha
|
2019-10-06 10:45:19 +02:00
|
|
|
*/
|
2020-09-08 20:41:01 +02:00
|
|
|
public final class Client implements EventListener, Closeable {
|
2019-12-31 10:57:11 +01:00
|
|
|
|
2020-02-04 19:46:18 +01:00
|
|
|
// Connection handling
|
2019-12-31 10:57:11 +01:00
|
|
|
private Socket socket;
|
|
|
|
private Receiver receiver;
|
|
|
|
private boolean online;
|
2019-10-06 10:45:19 +02:00
|
|
|
|
2020-02-04 19:46:18 +01:00
|
|
|
// Asynchronously initialized during handshake
|
2020-06-26 09:08:41 +02:00
|
|
|
private volatile User sender;
|
|
|
|
private volatile boolean rejected;
|
2019-10-06 10:45:19 +02:00
|
|
|
|
2020-04-10 11:01:03 +02:00
|
|
|
// Configuration, logging and event management
|
|
|
|
private static final ClientConfig config = ClientConfig.getInstance();
|
|
|
|
private static final Logger logger = EnvoyLog.getLogger(Client.class);
|
|
|
|
private static final EventBus eventBus = EventBus.getInstance();
|
2019-12-21 18:29:59 +01:00
|
|
|
|
2020-09-08 20:41:01 +02:00
|
|
|
/**
|
|
|
|
* Constructs a client and registers it as an event listener.
|
|
|
|
*
|
|
|
|
* @since Envoy Client v0.2-beta
|
|
|
|
*/
|
2020-10-19 18:17:51 +02:00
|
|
|
public Client() {
|
|
|
|
eventBus.registerListener(this);
|
|
|
|
}
|
2020-09-08 20:41:01 +02:00
|
|
|
|
2019-12-14 11:30:00 +01:00
|
|
|
/**
|
2020-10-19 18:17:51 +02:00
|
|
|
* Enters the online mode by acquiring a user ID from the server. As a connection has to be
|
|
|
|
* established and a handshake has to be made, this method will block for up to 5 seconds. If
|
|
|
|
* the handshake does exceed this time limit, an exception is thrown.
|
2019-12-21 18:29:59 +01:00
|
|
|
*
|
2020-07-09 09:12:41 +02:00
|
|
|
* @param credentials the login credentials of the user
|
2020-04-10 21:57:05 +02:00
|
|
|
* @throws TimeoutException if the server could not be reached
|
2020-06-13 22:36:52 +02:00
|
|
|
* @throws IOException if the login credentials could not be written
|
2020-10-19 18:17:51 +02:00
|
|
|
* @throws InterruptedException if the current thread is interrupted while waiting for the
|
|
|
|
* handshake response
|
2019-12-14 11:30:00 +01:00
|
|
|
*/
|
2020-11-30 22:19:45 +01:00
|
|
|
public void performHandshake(LoginCredentials credentials)
|
2020-10-19 18:17:51 +02:00
|
|
|
throws TimeoutException, IOException, InterruptedException {
|
|
|
|
if (online)
|
|
|
|
throw new IllegalStateException("Handshake has already been performed successfully");
|
2020-10-17 16:40:13 +02:00
|
|
|
rejected = false;
|
2020-06-26 09:08:41 +02:00
|
|
|
|
2019-12-31 10:57:11 +01:00
|
|
|
// Establish TCP connection
|
2020-10-19 18:17:51 +02:00
|
|
|
logger.log(Level.FINER, String.format("Attempting connection to server %s:%d...",
|
|
|
|
config.getServer(), config.getPort()));
|
2019-12-29 11:54:05 +01:00
|
|
|
socket = new Socket(config.getServer(), config.getPort());
|
2020-06-13 22:36:52 +02:00
|
|
|
logger.log(Level.FINE, "Successfully established TCP connection to server");
|
2019-12-29 11:54:05 +01:00
|
|
|
|
2020-06-09 15:41:01 +02:00
|
|
|
// Create object receiver
|
2019-12-31 10:57:11 +01:00
|
|
|
receiver = new Receiver(socket.getInputStream());
|
2019-11-04 23:10:53 +01:00
|
|
|
|
2020-09-19 11:37:42 +02:00
|
|
|
// Register user creation processor, contact list processor, message cache and
|
|
|
|
// authentication token
|
2020-06-26 09:08:41 +02:00
|
|
|
receiver.registerProcessor(User.class, sender -> this.sender = sender);
|
2020-02-12 07:53:24 +01:00
|
|
|
|
2019-12-31 15:38:52 +01:00
|
|
|
// Start receiver
|
2020-03-07 19:38:06 +01:00
|
|
|
receiver.start();
|
2019-12-31 15:38:52 +01:00
|
|
|
|
2019-12-31 10:57:11 +01:00
|
|
|
// Write login credentials
|
|
|
|
SerializationUtils.writeBytesWithLength(credentials, socket.getOutputStream());
|
|
|
|
|
2019-12-30 17:18:03 +01:00
|
|
|
// Wait for a maximum of five seconds to acquire the sender object
|
2020-06-09 22:24:09 +02:00
|
|
|
final long start = System.currentTimeMillis();
|
2020-04-02 22:03:43 +02:00
|
|
|
while (sender == null) {
|
2020-02-12 07:53:24 +01:00
|
|
|
|
|
|
|
// Quit immediately after handshake rejection
|
|
|
|
// This method can then be called again
|
|
|
|
if (rejected) {
|
|
|
|
socket.close();
|
|
|
|
receiver.removeAllProcessors();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-10-11 23:04:25 +02:00
|
|
|
if (System.currentTimeMillis() - start > 5000) {
|
2020-10-17 16:40:13 +02:00
|
|
|
rejected = true;
|
2020-11-30 22:19:45 +01:00
|
|
|
socket.close();
|
|
|
|
receiver.removeAllProcessors();
|
2020-10-11 23:04:25 +02:00
|
|
|
throw new TimeoutException("Did not log in after 5 seconds");
|
|
|
|
}
|
2019-12-30 17:18:03 +01:00
|
|
|
Thread.sleep(500);
|
2019-12-29 11:54:05 +01:00
|
|
|
}
|
|
|
|
|
2020-11-30 22:19:45 +01:00
|
|
|
// Remove handshake specific processors
|
2020-07-09 15:18:06 +02:00
|
|
|
receiver.removeAllProcessors();
|
|
|
|
|
2020-11-30 22:19:45 +01:00
|
|
|
online = true;
|
|
|
|
logger.log(Level.INFO, "Handshake completed.");
|
2020-07-09 15:18:06 +02:00
|
|
|
}
|
2020-02-06 21:03:08 +01:00
|
|
|
|
2019-12-31 10:57:11 +01:00
|
|
|
/**
|
2020-09-25 14:29:23 +02:00
|
|
|
* Sends an object to the server.
|
2020-01-29 07:44:25 +01:00
|
|
|
*
|
2020-09-25 14:29:23 +02:00
|
|
|
* @param obj the object to send
|
|
|
|
* @throws IllegalStateException if the client is not online
|
|
|
|
* @throws RuntimeException if the object serialization failed
|
|
|
|
* @since Envoy Client v0.2-beta
|
2019-12-31 10:57:11 +01:00
|
|
|
*/
|
2020-09-25 15:28:14 +02:00
|
|
|
public void send(Serializable obj) throws IllegalStateException, RuntimeException {
|
2020-09-25 14:29:23 +02:00
|
|
|
checkOnline();
|
|
|
|
logger.log(Level.FINE, "Sending " + obj);
|
|
|
|
try {
|
|
|
|
SerializationUtils.writeBytesWithLength(obj, socket.getOutputStream());
|
2020-10-11 23:04:25 +02:00
|
|
|
} catch (final IOException e) {
|
2020-09-25 14:29:23 +02:00
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
2019-12-31 10:57:11 +01:00
|
|
|
}
|
|
|
|
|
2020-02-05 07:09:25 +01:00
|
|
|
/**
|
2020-10-19 18:17:51 +02:00
|
|
|
* Sends a message to the server. The message's status will be incremented once it was delivered
|
|
|
|
* successfully.
|
2020-02-05 07:09:25 +01:00
|
|
|
*
|
2019-12-31 10:57:11 +01:00
|
|
|
* @param message the message to send
|
2020-09-24 18:00:59 +02:00
|
|
|
* @since Envoy Client v0.3-alpha
|
2020-02-05 07:09:25 +01:00
|
|
|
*/
|
2020-09-25 14:29:23 +02:00
|
|
|
public void sendMessage(Message message) {
|
|
|
|
send(message);
|
2020-01-02 16:11:41 +01:00
|
|
|
message.nextStatus();
|
2019-12-31 10:57:11 +01:00
|
|
|
}
|
2020-02-05 07:09:25 +01:00
|
|
|
|
2020-01-29 07:44:25 +01:00
|
|
|
/**
|
2020-03-26 16:06:18 +01:00
|
|
|
* Requests a new {@link IDGenerator} from the server.
|
2020-01-29 07:44:25 +01:00
|
|
|
*
|
2020-03-23 21:52:33 +01:00
|
|
|
* @since Envoy Client v0.3-alpha
|
2020-01-29 07:44:25 +01:00
|
|
|
*/
|
2020-09-25 15:28:14 +02:00
|
|
|
public void requestIDGenerator() {
|
2020-06-13 22:36:52 +02:00
|
|
|
logger.log(Level.INFO, "Requesting new id generator...");
|
2020-09-25 14:29:23 +02:00
|
|
|
send(new IDGeneratorRequest());
|
2020-01-29 07:44:25 +01:00
|
|
|
}
|
|
|
|
|
2020-09-25 15:56:08 +02:00
|
|
|
@Event(eventType = HandshakeRejection.class, priority = 1000)
|
2020-10-19 18:17:51 +02:00
|
|
|
private void onHandshakeRejection() {
|
|
|
|
rejected = true;
|
|
|
|
}
|
2020-09-08 20:41:01 +02:00
|
|
|
|
2019-12-31 10:57:11 +01:00
|
|
|
@Override
|
2020-10-17 16:40:13 +02:00
|
|
|
@Event(eventType = EnvoyCloseEvent.class, priority = 50)
|
2020-09-22 14:42:51 +02:00
|
|
|
public void close() {
|
|
|
|
if (online) {
|
|
|
|
logger.log(Level.INFO, "Closing connection...");
|
|
|
|
try {
|
2020-09-25 22:00:34 +02:00
|
|
|
|
|
|
|
// The sender must be reset as otherwise the handshake is immediately closed
|
|
|
|
sender = null;
|
|
|
|
online = false;
|
2020-09-22 14:42:51 +02:00
|
|
|
socket.close();
|
2020-09-23 23:11:32 +02:00
|
|
|
} catch (final IOException e) {
|
|
|
|
logger.log(Level.WARNING, "Failed to close socket: ", e);
|
|
|
|
}
|
2020-09-22 14:42:51 +02:00
|
|
|
}
|
|
|
|
}
|
2019-12-31 10:57:11 +01:00
|
|
|
|
2020-09-25 14:29:23 +02:00
|
|
|
/**
|
|
|
|
* Ensured that the client is online.
|
|
|
|
*
|
|
|
|
* @throws IllegalStateException if the client is not online
|
|
|
|
* @since Envoy Client v0.3-alpha
|
|
|
|
*/
|
2020-10-19 18:17:51 +02:00
|
|
|
private void checkOnline() throws IllegalStateException {
|
|
|
|
if (!online)
|
|
|
|
throw new IllegalStateException("Client is not online");
|
|
|
|
}
|
2019-12-31 15:38:52 +01:00
|
|
|
|
2019-10-12 17:35:58 +02:00
|
|
|
/**
|
2020-06-26 09:08:41 +02:00
|
|
|
* @return the {@link User} as which this client is logged in
|
2020-03-23 21:52:33 +01:00
|
|
|
* @since Envoy Client v0.1-alpha
|
2019-10-12 17:35:58 +02:00
|
|
|
*/
|
2019-10-19 12:10:52 +02:00
|
|
|
public User getSender() { return sender; }
|
|
|
|
|
2019-12-14 11:30:00 +01:00
|
|
|
/**
|
|
|
|
* Sets the client user which is used to send messages.
|
2019-12-21 18:29:59 +01:00
|
|
|
*
|
2020-04-02 22:03:43 +02:00
|
|
|
* @param clientUser the client user to set
|
2020-03-23 21:52:33 +01:00
|
|
|
* @since Envoy Client v0.2-alpha
|
2019-12-14 11:30:00 +01:00
|
|
|
*/
|
2020-06-13 22:36:52 +02:00
|
|
|
public void setSender(User clientUser) { sender = clientUser; }
|
2019-12-14 10:53:20 +01:00
|
|
|
|
2019-12-31 10:57:11 +01:00
|
|
|
/**
|
|
|
|
* @return the {@link Receiver} used by this {@link Client}
|
2020-09-24 18:00:59 +02:00
|
|
|
* @since v0.2-alpha
|
2019-12-31 10:57:11 +01:00
|
|
|
*/
|
|
|
|
public Receiver getReceiver() { return receiver; }
|
|
|
|
|
2019-12-11 18:52:30 +01:00
|
|
|
/**
|
|
|
|
* @return {@code true} if a connection to the server could be established
|
2020-03-23 21:52:33 +01:00
|
|
|
* @since Envoy Client v0.2-alpha
|
2019-12-11 18:52:30 +01:00
|
|
|
*/
|
|
|
|
public boolean isOnline() { return online; }
|
2020-02-07 15:41:17 +01:00
|
|
|
}
|