Улучшена передача контекста (не нарушается принцип ответственности), добавлено логгирование, переработан Boot который собирает все древо приложения, улучшен протокол

This commit is contained in:
RoyceDa
2026-02-03 20:11:37 +02:00
parent 695ec9c520
commit cb5cafb8f6
12 changed files with 188 additions and 106 deletions

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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 + "Время последней активности устройств клиентов обновлено.");
}
}

View 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;
};
}
}

View 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";
}

View 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();
}
}

View File

@@ -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);
}));
}

View File

@@ -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 Пакет, полученный от клиента.

View File

@@ -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<>();
}