Улучшена передача контекста (не нарушается принцип ответственности), добавлено логгирование, переработан Boot который собирает все древо приложения, улучшен протокол
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
package com.rosetta.im;
|
||||
|
||||
import com.rosetta.im.event.EventManager;
|
||||
|
||||
import io.orprotocol.Context;
|
||||
|
||||
/**
|
||||
* Контекст приложения, хранящий глобальные объекты
|
||||
*/
|
||||
public class AppContext extends Context {
|
||||
|
||||
private EventManager eventManager;
|
||||
|
||||
public AppContext(EventManager eventManager) {
|
||||
this.eventManager = eventManager;
|
||||
}
|
||||
|
||||
public EventManager getEventManager() {
|
||||
return eventManager;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +1,81 @@
|
||||
package com.rosetta.im;
|
||||
|
||||
import com.rosetta.im.event.EventManager;
|
||||
import com.rosetta.im.executors.Executor0Handshake;
|
||||
import com.rosetta.im.listeners.ServerStopListener;
|
||||
import com.rosetta.im.logger.Logger;
|
||||
import com.rosetta.im.logger.enums.Color;
|
||||
import com.rosetta.im.logger.enums.LogLevel;
|
||||
import com.rosetta.im.packet.Packet0Handshake;
|
||||
|
||||
import io.orprotocol.packet.PacketManager;
|
||||
|
||||
/**
|
||||
* Boot отвечает за инициализацию всех пакетов и их обработчиков,
|
||||
* а так же событий приложения. Этот Boot отвечает за приложение, а не за протокол.
|
||||
*/
|
||||
public class Boot {
|
||||
|
||||
private PacketManager packetManager;
|
||||
private EventManager eventManager;
|
||||
private Logger logger;
|
||||
|
||||
public Boot() {
|
||||
this.packetManager = new PacketManager();
|
||||
this.eventManager = new EventManager();
|
||||
this.logger = new Logger(LogLevel.INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить менеджер пакетов приложения
|
||||
* @return PacketManager
|
||||
*/
|
||||
public PacketManager getPacketManager() {
|
||||
return this.packetManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализация всех пакетов и их обработчиков
|
||||
* Получить менеджер событий приложения
|
||||
* @return EventManager
|
||||
*/
|
||||
public EventManager getEventManager() {
|
||||
return this.eventManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить логгер приложения
|
||||
* @return Logger
|
||||
*/
|
||||
public Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрация пакетов, обработчиков, событий приложения
|
||||
* @return Boot
|
||||
*/
|
||||
public Boot bootstrap() {
|
||||
this.registerAllPackets();
|
||||
this.registerAllExecutors();
|
||||
this.registerAllEvents();
|
||||
this.printBootMessage();
|
||||
return this;
|
||||
}
|
||||
|
||||
private void registerAllEvents() {
|
||||
this.eventManager.registerListener(new ServerStopListener(this.logger));
|
||||
}
|
||||
|
||||
private void registerAllPackets() {
|
||||
this.packetManager.registerPacket(0, Packet0Handshake.class);
|
||||
}
|
||||
|
||||
private void registerAllExecutors() {
|
||||
this.packetManager.registerExecutor(0, Executor0Handshake.class);
|
||||
this.packetManager.registerExecutor(0, new Executor0Handshake(this.eventManager));
|
||||
}
|
||||
|
||||
private void printBootMessage() {
|
||||
System.out.println("Bootstrapping completed. All packets and executors are registered.");
|
||||
System.out.println("Total packets registered: " + this.packetManager.totalPackets());
|
||||
System.out.println("Total executors registered: " + this.packetManager.totalExecutors());
|
||||
this.logger.log(LogLevel.INFO, Color.GREEN + "Boot successful complete");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.rosetta.im;
|
||||
|
||||
import com.rosetta.im.event.EventManager;
|
||||
import com.rosetta.im.listeners.ServerStopListener;
|
||||
|
||||
public class ContextBuilder {
|
||||
|
||||
private AppContext appContext;
|
||||
|
||||
public ContextBuilder() {}
|
||||
|
||||
public AppContext buildContext() {
|
||||
/**
|
||||
* Создание глобальных объектов приложения
|
||||
*/
|
||||
EventManager eventManager = new EventManager();
|
||||
eventManager.registerListener(new ServerStopListener());
|
||||
|
||||
this.appContext = new AppContext(eventManager);
|
||||
return this.appContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,25 +2,27 @@ package com.rosetta.im;
|
||||
|
||||
import io.orprotocol.Server;
|
||||
import io.orprotocol.Settings;
|
||||
import io.orprotocol.packet.PacketManager;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
/**
|
||||
* Регистрация всех пакетов и их обработчиков
|
||||
*/
|
||||
Boot boot = new Boot();
|
||||
PacketManager manager = boot.bootstrap().getPacketManager();
|
||||
Boot boot = new Boot().bootstrap();
|
||||
/**
|
||||
* Загрузка настроек сервера, сборка контекста приложения
|
||||
* Загрузка настроек сервера
|
||||
*/
|
||||
Settings settings = new Settings(8881, 30);
|
||||
AppContext appContext = new ContextBuilder().buildContext();
|
||||
ServerAdapter serverAdapter = new ServerAdapter(appContext.getEventManager());
|
||||
/**
|
||||
* Запуск сервера на порту 8881
|
||||
* Создание адаптера сервера для трансляции событий протокола в события приложения,
|
||||
* обрабатываются основыне события такие как запуск/остановка сервера,
|
||||
* подключение/отключение клиентов, ошибки сервера и получение пакетов
|
||||
*/
|
||||
Server server = new Server(settings, manager, appContext, serverAdapter);
|
||||
ServerAdapter serverAdapter = new ServerAdapter(boot.getEventManager());
|
||||
/**
|
||||
* Запуск сервера
|
||||
*/
|
||||
Server server = new Server(settings, boot.getPacketManager(), serverAdapter);
|
||||
server.start();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.rosetta.im.executors;
|
||||
|
||||
import com.rosetta.im.AppContext;
|
||||
import com.rosetta.im.Configuration;
|
||||
import com.rosetta.im.Failures;
|
||||
import com.rosetta.im.client.tags.ECIAuthentificate;
|
||||
@@ -9,6 +8,7 @@ import com.rosetta.im.database.entity.Device;
|
||||
import com.rosetta.im.database.entity.User;
|
||||
import com.rosetta.im.database.repository.DeviceRepository;
|
||||
import com.rosetta.im.database.repository.UserRepository;
|
||||
import com.rosetta.im.event.EventManager;
|
||||
import com.rosetta.im.event.events.handshake.HandshakeCompletedEvent;
|
||||
import com.rosetta.im.event.events.handshake.HandshakeDeviceConfirmEvent;
|
||||
import com.rosetta.im.event.events.handshake.HandshakeFailedEvent;
|
||||
@@ -27,6 +27,11 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
private final UserRepository userRepository = new UserRepository();
|
||||
private final DeviceRepository deviceRepository = new DeviceRepository();
|
||||
private final DeviceService deviceService = new DeviceService(deviceRepository);
|
||||
private final EventManager eventManager;
|
||||
|
||||
public Executor0Handshake(EventManager eventManager) {
|
||||
this.eventManager = eventManager;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@@ -39,7 +44,6 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
String deviceName = handshake.getDeviceName();
|
||||
String deviceOs = handshake.getDeviceOs();
|
||||
int protocolVersion = handshake.getProtocolVersion();
|
||||
AppContext context = (AppContext) this.getContext();
|
||||
/**
|
||||
* Получаем информацию об аутентификации клиента
|
||||
* используя возможности ECI тэгов.
|
||||
@@ -95,7 +99,7 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
/**
|
||||
* Вызываем событие завершения хэндшейка
|
||||
*/
|
||||
boolean cancelled = context.getEventManager().callEvent(
|
||||
boolean cancelled = this.eventManager.callEvent(
|
||||
new HandshakeCompletedEvent(publicKey, privateKey, device, eciTag, client)
|
||||
);
|
||||
if(cancelled) {
|
||||
@@ -120,7 +124,7 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
/**
|
||||
* Приватный ключ не совпадает, отключаем клиента
|
||||
*/
|
||||
context.getEventManager().callEvent(new HandshakeFailedEvent(publicKey, privateKey, device, authentificate, client));
|
||||
eventManager.callEvent(new HandshakeFailedEvent(publicKey, privateKey, device, authentificate, client));
|
||||
client.disconnect(Failures.AUTHENTIFICATION_ERROR);
|
||||
return;
|
||||
}
|
||||
@@ -139,7 +143,7 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
/**
|
||||
* Вызываем событие подтверждения устройства
|
||||
*/
|
||||
context.getEventManager().callEvent(
|
||||
this.eventManager.callEvent(
|
||||
new HandshakeDeviceConfirmEvent(publicKey, privateKey, device, authentificate, client)
|
||||
);
|
||||
/**
|
||||
@@ -179,7 +183,7 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
/**
|
||||
* Вызываем событие завершения хэндшейка
|
||||
*/
|
||||
boolean cancelled = context.getEventManager().callEvent(
|
||||
boolean cancelled = this.eventManager.callEvent(
|
||||
new HandshakeCompletedEvent(publicKey, privateKey, device, eciTag, client)
|
||||
);
|
||||
if(cancelled) {
|
||||
|
||||
@@ -6,6 +6,8 @@ import com.rosetta.im.database.repository.DeviceRepository;
|
||||
import com.rosetta.im.event.EventHandler;
|
||||
import com.rosetta.im.event.Listener;
|
||||
import com.rosetta.im.event.events.ServerStopEvent;
|
||||
import com.rosetta.im.logger.Logger;
|
||||
import com.rosetta.im.logger.enums.Color;
|
||||
import com.rosetta.im.service.services.DeviceService;
|
||||
|
||||
import io.orprotocol.Server;
|
||||
@@ -17,25 +19,39 @@ import io.orprotocol.client.Client;
|
||||
*/
|
||||
public class ServerStopListener implements Listener {
|
||||
|
||||
private static final DeviceRepository deviceRepository = new DeviceRepository();
|
||||
private static final DeviceService deviceService = new DeviceService(deviceRepository);
|
||||
private final DeviceRepository deviceRepository = new DeviceRepository();
|
||||
private final DeviceService deviceService = new DeviceService(deviceRepository);
|
||||
private Logger logger;
|
||||
|
||||
public ServerStopListener(Logger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
@EventHandler
|
||||
public void onServerStop(ServerStopEvent event) {
|
||||
Server server = event.getServer();
|
||||
System.out.println("Server is stopping. Please wait...");
|
||||
this.logger.info(Color.RED + "Сервер останавливается, обновляем время последней активности устройств клиентов...");
|
||||
for(Client client : server.getClients()){
|
||||
ECIAuthentificate eciAuth = client.getTag(ECIAuthentificate.class);
|
||||
if(eciAuth == null || !eciAuth.hasAuthorized()){
|
||||
/**
|
||||
* Если клиент не авторизован, пропускаем его, таким клиентам не нужно
|
||||
* обновлять время активности устройства
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
ECIDevice eciDevice = client.getTag(ECIDevice.class);
|
||||
if(eciDevice == null){
|
||||
/**
|
||||
* Если у клиента нет тега устройства, пропускаем его
|
||||
* такого быть не должно, но на всякий случай
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
deviceService.updateDeviceLeaveTime(eciDevice.getDeviceId());
|
||||
}
|
||||
System.out.println("Server stopped successfully.");
|
||||
this.logger.info(Color.RED + "Время последней активности устройств клиентов обновлено.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
74
src/main/java/com/rosetta/im/logger/Logger.java
Normal file
74
src/main/java/com/rosetta/im/logger/Logger.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package com.rosetta.im.logger;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import com.rosetta.im.logger.enums.Color;
|
||||
import com.rosetta.im.logger.enums.LogLevel;
|
||||
|
||||
public class Logger {
|
||||
|
||||
private long startTime = 0;
|
||||
private LogLevel logLevel;
|
||||
|
||||
public Logger(LogLevel logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
startTime = (System.currentTimeMillis() / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Логирование сообщения с указанным уровнем логирования
|
||||
* @param logLevel уровень логирования
|
||||
* @param message сообщение для логирования
|
||||
*/
|
||||
public void log(LogLevel logLevel, String message) {
|
||||
if (!this.logLevel.allows(logLevel)) {
|
||||
return;
|
||||
}
|
||||
long currentTimeMs = System.currentTimeMillis();
|
||||
long currentTime = currentTimeMs / 1000;
|
||||
long elapsedTime = currentTime - startTime;
|
||||
String currentDateISO = Instant.ofEpochMilli(currentTimeMs).toString();
|
||||
System.out.println(getColorForLogLevel(logLevel) + "["+ logLevel.toString() +"]" + Color.RESET + "[" + currentDateISO + "]" + Color.CYAN + "[+" + elapsedTime + "] " + Color.WHITE + message + Color.RESET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Логирование информационного сообщения
|
||||
* @param message сообщение для логирования
|
||||
*/
|
||||
public void info(String message) {
|
||||
this.log(LogLevel.INFO, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Логирование предупреждающего сообщения
|
||||
* @param message сообщение для логирования
|
||||
*/
|
||||
public void warn(String message) {
|
||||
this.log(LogLevel.WARN, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Логирование сообщения об ошибке
|
||||
* @param message сообщение для логирования
|
||||
*/
|
||||
public void error(String message) {
|
||||
this.log(LogLevel.ERROR, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Логирование отладочного сообщения
|
||||
* @param message сообщение для логирования
|
||||
*/
|
||||
public void debug(String message) {
|
||||
this.log(LogLevel.DEBUG, message);
|
||||
}
|
||||
|
||||
private String getColorForLogLevel(LogLevel logLevel) {
|
||||
return switch (logLevel) {
|
||||
case INFO -> Color.BLUE;
|
||||
case WARN -> Color.YELLOW;
|
||||
case ERROR -> Color.RED;
|
||||
case DEBUG -> Color.PURPLE;
|
||||
};
|
||||
}
|
||||
}
|
||||
13
src/main/java/com/rosetta/im/logger/enums/Color.java
Normal file
13
src/main/java/com/rosetta/im/logger/enums/Color.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.rosetta.im.logger.enums;
|
||||
|
||||
public final class Color {
|
||||
public static final String RESET = "\u001B[0m";
|
||||
public static final String BLACK = "\u001B[30m";
|
||||
public static final String RED = "\u001B[31m";
|
||||
public static final String GREEN = "\u001B[32m";
|
||||
public static final String YELLOW = "\u001B[33m";
|
||||
public static final String BLUE = "\u001B[34m";
|
||||
public static final String PURPLE = "\u001B[35m";
|
||||
public static final String CYAN = "\u001B[36m";
|
||||
public static final String WHITE = "\u001B[37m";
|
||||
}
|
||||
12
src/main/java/com/rosetta/im/logger/enums/LogLevel.java
Normal file
12
src/main/java/com/rosetta/im/logger/enums/LogLevel.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.rosetta.im.logger.enums;
|
||||
|
||||
public enum LogLevel {
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR,
|
||||
DEBUG;
|
||||
|
||||
public boolean allows(LogLevel other) {
|
||||
return this.ordinal() <= other.ordinal();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ public class Server extends WebSocketServer {
|
||||
private PacketManager packetManager;
|
||||
private Settings settings;
|
||||
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||
private Context context;
|
||||
private ServerListener listener;
|
||||
private ThreadLocker threadLocker = new ThreadLocker();
|
||||
|
||||
@@ -38,33 +37,17 @@ public class Server extends WebSocketServer {
|
||||
this.packetManager = packetManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Конструктор сервера с объектом прикрепления
|
||||
* @param settings базовые настройки серверера
|
||||
* @param packetManager менеджер пакетов (обработчиков и зарегистрированных пакетов)
|
||||
* @param context вложение которое будет передаваться всем серрверным обработчикам пакетов,
|
||||
* может быть использовано для передачи контекста приложения
|
||||
*/
|
||||
public Server(Settings settings, PacketManager packetManager, Context context) {
|
||||
super(new InetSocketAddress(settings.port));
|
||||
this.settings = settings;
|
||||
this.packetManager = packetManager;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Конструктор сервера с объектом прикрепления и слушателем событий сервера
|
||||
* @param settings базовые настройки серверера
|
||||
* @param packetManager менеджер пакетов (обработчиков и зарегистрированных пакетов)
|
||||
* @param context вложение которое будет передаваться всем серрверным обработчикам пакетов,
|
||||
* @param listener слушатель событий сервера
|
||||
* может быть использовано для передачи контекста приложения
|
||||
*/
|
||||
public Server(Settings settings, PacketManager packetManager, Context context, ServerListener listener) {
|
||||
public Server(Settings settings, PacketManager packetManager, ServerListener listener) {
|
||||
super(new InetSocketAddress(settings.port));
|
||||
this.settings = settings;
|
||||
this.packetManager = packetManager;
|
||||
this.context = context;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@@ -138,10 +121,8 @@ public class Server extends WebSocketServer {
|
||||
/**
|
||||
* Получаем обработчик пакета и вызываем его метод обработки.
|
||||
*/
|
||||
Class<? extends PacketExecutor> executorClass = this.packetManager.getExecutors().get(packetId);
|
||||
PacketExecutor executor = executorClass.getConstructor().newInstance();
|
||||
PacketExecutor executor = this.packetManager.getExecutors().get(packetId);
|
||||
executor.settings = this.settings;
|
||||
executor.context = this.context;
|
||||
if(listener != null && !listener.onPacketReceived(this, client, packet)) {
|
||||
/**
|
||||
* Если слушатель сервера вернул false, пакет не обрабатываем.
|
||||
@@ -151,7 +132,7 @@ public class Server extends WebSocketServer {
|
||||
/**
|
||||
* Проверяем наличие блокировки для данного пакета и ключа в аннотации @Lock.
|
||||
*/
|
||||
if(!threadLocker.acquireLock(packet, executorClass)) {
|
||||
if(!threadLocker.acquireLock(packet, executor.getClass())) {
|
||||
/**
|
||||
* Если блокировка уже существует, значит другой поток обрабатывает пакет
|
||||
* с таким же значением lockFor, отклоняем текущий пакет.
|
||||
@@ -164,7 +145,7 @@ public class Server extends WebSocketServer {
|
||||
/**
|
||||
* Снимаем блокировку после обработки пакета.
|
||||
*/
|
||||
threadLocker.releaseLock(packet, executorClass);
|
||||
threadLocker.releaseLock(packet, executor.getClass());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error while processing packet " + packetClass.getName());
|
||||
@@ -244,7 +225,6 @@ public class Server extends WebSocketServer {
|
||||
/**
|
||||
* Останавливаем сервер при завершении работы и вызываем слушатели остановки сервера.
|
||||
*/
|
||||
System.out.println("JVM Shutdown detected, stopping server...");
|
||||
this.listener.onServerStop(this);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.orprotocol.packet;
|
||||
|
||||
import io.orprotocol.Context;
|
||||
import io.orprotocol.ProtocolException;
|
||||
import io.orprotocol.Settings;
|
||||
import io.orprotocol.client.Client;
|
||||
@@ -10,7 +9,6 @@ import io.orprotocol.client.Client;
|
||||
*/
|
||||
public abstract class PacketExecutor {
|
||||
public Settings settings;
|
||||
public Context context;
|
||||
|
||||
/**
|
||||
* Настройки сервера.
|
||||
@@ -19,15 +17,6 @@ public abstract class PacketExecutor {
|
||||
public Settings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Контекст приложения переданный при создании сервера.
|
||||
* @return контекст
|
||||
*/
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вызывается при получении пакета от клиента.
|
||||
* @param packet Пакет, полученный от клиента.
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.HashMap;
|
||||
public class PacketManager {
|
||||
|
||||
private HashMap<Integer, Class<? extends Packet>> packets;
|
||||
private HashMap<Integer, Class<? extends PacketExecutor>> executors;
|
||||
private HashMap<Integer, PacketExecutor> executors;
|
||||
|
||||
public PacketManager() {
|
||||
this.packets = new HashMap<>();
|
||||
@@ -46,7 +46,7 @@ public class PacketManager {
|
||||
* Возвращает зарегистрированные исполнители пакетов.
|
||||
* @return Хэш-карта зарегистрированных исполнителей пакетов.
|
||||
*/
|
||||
public HashMap<Integer, Class<? extends PacketExecutor>> getExecutors() {
|
||||
public HashMap<Integer, PacketExecutor> getExecutors() {
|
||||
return this.executors;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class PacketManager {
|
||||
* @param packetId ID пакета
|
||||
* @param executor Обработчик пакета
|
||||
*/
|
||||
public void registerExecutor(int packetId, Class <? extends PacketExecutor> executor) {
|
||||
public void registerExecutor(int packetId, PacketExecutor executor) {
|
||||
if (this.executors == null) {
|
||||
this.executors = new HashMap<>();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user