Чат (11) Пришло время написать главный метод класса Handler, который будет вызывать все вспомогательные методы, написанные ранее. Реализуем метод void run() в классе Handler. Он должен: 1. Выводить сообщение, что установлено новое соединение с удаленным адресом, который можно получить с помощью метода getRemoteSocketAddress. 2. Создавать Connection, используя поле socket. 3. Вызывать метод, реализующий рукопожатие с клиентом, сохраняя имя нового клиента. 4. Рассылать всем участникам чата информацию об имени присоединившегося участника (сообщение с типом USER_ADDED). Подумай, какой метод подойдет для этого лучше всего. 5. Сообщать новому участнику о существующих участниках. 6. Запускать главный цикл обработки сообщений сервером. 7. Обеспечить закрытие соединения при возникновении исключения. 8. Отловить все исключения типа IOException и ClassNotFoundException, вывести в консоль информацию, что произошла ошибка при обмене данными с удаленным адресом. 9. После того как все исключения обработаны, если п.11.3 отработал и возвратил нам имя, мы должны удалить запись для этого имени из connectionMap и разослать всем остальным участникам сообщение с типом USER_REMOVED и сохраненным именем. 10. Последнее, что нужно сделать в методе run() – вывести сообщение, информирующее что соединение с удаленным адресом закрыто. Наш сервер полностью готов. Попробуй его запустить. Требования: 1. Метод run должен выводить на экран сообщение о том, что было установлено соединение с удаленным адресом (используя метод getRemoteSocketAddress). 2. Метод run должен создавать новое соединение (connection) используя в качестве параметра поле socket. 3. Метод run должен вызывать метод serverHandshake используя в качестве параметра только что созданное соединение; результатом будет имя пользователя (userName). 4. Метод run должен вызывать метод sendBroadcastMessage используя в качестве параметра новое сообщение (MessageType.USER_ADDED, userName). 5. Метод run должен вызывать метод sendListOfUsers используя в качестве параметров connection и userName. 6. Метод run должен вызывать метод serverMainLoop используя в качестве параметров connection и userName. 7. Прежде чем завершиться, метод run должен удалять из connectionMap запись соответствующую userName, и отправлять всем участникам чата сообщение о том, что текущий пользователь был удален. 8. Метод run должен корректно обрабатывать исключения IOException и ClassNotFoundException.
package com.javarush.task.task30.task3008;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Server {
    private static Map<String, Connection> connectionMap = new ConcurrentHashMap<>();
    public static void main( String [] args) throws IOException {

        ConsoleHelper.writeMessage("Введите порт сервера:");
        int port = ConsoleHelper.readInt();
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            ConsoleHelper.writeMessage("Сервер запущен на порту: " + port);
            while (true) {
                Socket socket = serverSocket.accept();
                new Handler(socket).start();
            }
        } catch (IOException e) {
            ConsoleHelper.writeMessage(e.getMessage());
        }

    }
    public static  void sendBroadcastMessage(Message message){
        for (String clientName : connectionMap.keySet()) {
            try {
                connectionMap.get(clientName).send(message);
            } catch (IOException e) {
                ConsoleHelper.writeMessage("Не могу отправить сообщение клиенту с именем: " + clientName);
            }
        }
    }


    private static class Handler extends  Thread {
        private Socket socket;

        @Override
        public void run() {

            ConsoleHelper.writeMessage("Установлено соединение с адресом " + socket.getRemoteSocketAddress());
            String userName = null;

            try (Connection connection = new Connection(socket)) {
                ConsoleHelper.writeMessage("Подключение к порту: " + connection.getRemoteSocketAddress());
                userName = serverHandshake(connection);
                sendBroadcastMessage(new Message(MessageType.USER_ADDED, userName));
                sendListOfUsers(connection, userName);
                serverMainLoop(connection, userName);
            } catch (IOException e) {
                ConsoleHelper.writeMessage("Error");
            } catch (ClassNotFoundException e) {
                ConsoleHelper.writeMessage("Error");
            }
            connectionMap.remove(userName);
            sendBroadcastMessage(new Message(MessageType.USER_REMOVED, userName));
            ConsoleHelper.writeMessage("Соединение с удаленным адресом закрыто");
        }

        private String serverHandshake(Connection connection) throws IOException, ClassNotFoundException {
            while (true) {
                connection.send(new Message(MessageType.NAME_REQUEST));
                Message answer = connection.receive();

                if (answer.getType() == MessageType.USER_NAME) {

                    if (!answer.getData().isEmpty()) {
                        if (!connectionMap.containsKey(answer.getData())) {
                            connectionMap.put(answer.getData(), connection);
                            connection.send(new Message(MessageType.NAME_ACCEPTED));
                            return answer.getData();
                        }
                    }
                }
            }
        }

        private void sendListOfUsers(Connection connection, String userName) throws IOException{
            for(Map.Entry<String, Connection> pair : connectionMap.entrySet()) {
                if (!connectionMap.containsKey(userName)) {
                    connection.send( new Message(MessageType.USER_ADDED, pair.getKey()));
                }
            }
        }

        private void serverMainLoop(Connection connection, String userName) throws IOException, ClassNotFoundException{
            while (true) {
                Message message = connection.receive();
                try {
                    if (message != null && message.getType() == (MessageType.TEXT)) {
                        sendBroadcastMessage(new Message(MessageType.TEXT, userName + ": " + message.getData()));
                    }
                } catch (Exception e) {
                    ConsoleHelper.writeMessage("Error");
                }
            }
        }

        public Handler(Socket socket) {
            this.socket = socket;
        }
    }
}