home / connvers / src / main / java / avividi / connvers / client / Client.java

Client.java



package avividi.connvers.client;

import avividi.connvers.protocol.MessageEncoder;
import avividi.connvers.protocol.MessageTopic;
import avividi.connvers.protocol.Message;
import avividi.connvers.UserInputHandler;
import avividi.connvers.CommandInterpreter;
import avividi.connvers.protocol.ValidationException;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static avividi.connvers.Connvers.HEARTBEAT_FREQUENCY_MS;
import static avividi.connvers.Connvers.checkAndPrintNewDay;
import static avividi.connvers.Connvers.printSystemLine;

public class Client {

  private final String user;
  private final int port;
  private final String host;
  private final MessageEncoder messageEncoder;

  public Client(String user, int port, String host, MessageEncoder messageEncoder) {
    this.user = user;
    this.port = port;
    this.host = host;
    this.messageEncoder = messageEncoder;
  }

  public void start() {

    try(
        Socket clientSocket = new Socket(host, port);
        //create output stream attached to socket
        DataOutputStream outStream = new DataOutputStream(clientSocket.getOutputStream());
        //create input stream attached to socket
        DataInputStream inStream = new DataInputStream(clientSocket.getInputStream())
    ) {
      handshake(outStream, inStream);

      // handler for user input
      new Thread(new UserInputHandler(
          user,
          true,
          m -> sendMessage(outStream, m),
          new CommandInterpreter() {})
      ).start();

      // schedule heartbeat messages
      Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
          try {
            sendMessage(outStream, new Message(user, Instant.now(), MessageTopic.heartbeat, null));
          } catch (IOException e) {
            e.printStackTrace();
            System.exit(0);
          }
      }, HEARTBEAT_FREQUENCY_MS, HEARTBEAT_FREQUENCY_MS, TimeUnit.MILLISECONDS);

      // listen for incoming messages
      Message prevMessage = null;
      while (!Thread.currentThread().isInterrupted()) {
        String rawMessage = inStream.readUTF();
        Message message = messageEncoder.decode(rawMessage);
        checkAndPrintNewDay(message, prevMessage);
        prevMessage = message;
        message.print();
      }
    }
    catch (IOException e) {
      printSystemLine("Disconnected from server.");
      System.exit(0);
    }
    catch (Exception e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  private synchronized void sendMessage(DataOutputStream outStream, Message message) throws IOException {
    outStream.writeUTF(messageEncoder.encode(message));
  }

  private void handshake(DataOutputStream outStream, DataInputStream inStream) throws IOException, ValidationException {
    outStream.writeUTF(messageEncoder.encode(new Message(user, Instant.now(), MessageTopic.join, null)));
    boolean welcome = false;
    while (!welcome) {
      String rawMessage = inStream.readUTF();
      Message message = messageEncoder.decode(rawMessage);
      welcome = message.topic() == MessageTopic.welcome;

      if (message.topic() == MessageTopic.error) {
        printSystemLine(String.format("Server error: %s", message.content()));
      }
    }
  }
}