Хэндшейк, улучшенный граф, новые методы в репозитории в DB
This commit is contained in:
@@ -1,13 +1,20 @@
|
||||
package com.rosetta.im;
|
||||
|
||||
import com.rosetta.im.client.ClientManager;
|
||||
import com.rosetta.im.event.EventManager;
|
||||
import com.rosetta.im.executors.Executor0Handshake;
|
||||
import com.rosetta.im.listeners.HandshakeCompleteListener;
|
||||
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 com.rosetta.im.packet.Packet1UserInfo;
|
||||
import com.rosetta.im.packet.Packet2Result;
|
||||
import com.rosetta.im.packet.Packet3Search;
|
||||
|
||||
import io.orprotocol.Server;
|
||||
import io.orprotocol.Settings;
|
||||
import io.orprotocol.packet.PacketManager;
|
||||
|
||||
/**
|
||||
@@ -19,11 +26,20 @@ public class Boot {
|
||||
private PacketManager packetManager;
|
||||
private EventManager eventManager;
|
||||
private Logger logger;
|
||||
private Server server;
|
||||
private ServerAdapter serverAdapter;
|
||||
private ClientManager clientManager;
|
||||
|
||||
public Boot() {
|
||||
this.packetManager = new PacketManager();
|
||||
this.eventManager = new EventManager();
|
||||
this.logger = new Logger(LogLevel.INFO);
|
||||
this.serverAdapter = new ServerAdapter(this.eventManager);
|
||||
this.server = new Server(new Settings(
|
||||
8881,
|
||||
30
|
||||
), packetManager, this.serverAdapter);
|
||||
this.clientManager = new ClientManager(server);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,6 +58,14 @@ public class Boot {
|
||||
return this.eventManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить менеджера клиентов, нужно для того чтобы отправить пакет списку клиентов например
|
||||
* @return менеджер клиентов
|
||||
*/
|
||||
public ClientManager getClientManager() {
|
||||
return this.clientManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить логгер приложения
|
||||
* @return Logger
|
||||
@@ -64,10 +88,14 @@ public class Boot {
|
||||
|
||||
private void registerAllEvents() {
|
||||
this.eventManager.registerListener(new ServerStopListener(this.logger));
|
||||
this.eventManager.registerListener(new HandshakeCompleteListener());
|
||||
}
|
||||
|
||||
private void registerAllPackets() {
|
||||
this.packetManager.registerPacket(0, Packet0Handshake.class);
|
||||
this.packetManager.registerPacket(1, Packet1UserInfo.class);
|
||||
this.packetManager.registerPacket(2, Packet2Result.class);
|
||||
this.packetManager.registerPacket(3, Packet3Search.class);
|
||||
}
|
||||
|
||||
private void registerAllExecutors() {
|
||||
|
||||
@@ -8,6 +8,10 @@ public enum Failures implements BaseFailures {
|
||||
*/
|
||||
AUTHENTIFICATION_ERROR(3001),
|
||||
DATA_MISSMATCH(3001),
|
||||
/**
|
||||
* Handshake не завершен
|
||||
*/
|
||||
HANDSHAKE_NOT_COMPLETED(3002),
|
||||
/**
|
||||
* Неподдерживаемый протокол
|
||||
*/
|
||||
|
||||
@@ -1,28 +1,14 @@
|
||||
package com.rosetta.im;
|
||||
|
||||
import io.orprotocol.Server;
|
||||
import io.orprotocol.Settings;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
/**
|
||||
* Регистрация всех пакетов и их обработчиков
|
||||
*/
|
||||
Boot boot = new Boot().bootstrap();
|
||||
Boot boot = new Boot();
|
||||
/**
|
||||
* Загрузка настроек сервера
|
||||
* Стартуем сервер
|
||||
*/
|
||||
Settings settings = new Settings(8881, 30);
|
||||
/**
|
||||
* Создание адаптера сервера для трансляции событий протокола в события приложения,
|
||||
* обрабатываются основыне события такие как запуск/остановка сервера,
|
||||
* подключение/отключение клиентов, ошибки сервера и получение пакетов
|
||||
*/
|
||||
ServerAdapter serverAdapter = new ServerAdapter(boot.getEventManager());
|
||||
/**
|
||||
* Запуск сервера
|
||||
*/
|
||||
Server server = new Server(settings, boot.getPacketManager(), serverAdapter);
|
||||
server.start();
|
||||
boot.bootstrap();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
package com.rosetta.im.client.tags;
|
||||
|
||||
import com.rosetta.im.packet.enums.HandshakeStage;
|
||||
import java.util.Map;
|
||||
|
||||
import com.rosetta.im.packet.runtime.HandshakeStage;
|
||||
|
||||
import io.orprotocol.client.ECITag;
|
||||
|
||||
/**
|
||||
* Это вложенный обьект для клиента, содержащий информацию об аутентификации.
|
||||
*/
|
||||
public class ECIAuthentificate extends ECITag {
|
||||
public class ECIAuthentificate implements ECITag {
|
||||
|
||||
public String publicKey;
|
||||
public String privateKey;
|
||||
@@ -51,4 +53,12 @@ public class ECIAuthentificate extends ECITag {
|
||||
return this.handshakeStage == HandshakeStage.COMPLETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаем индекс для быстрого поиска клиентов (индексацию реализует ORProtocol)
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> getIndex() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.rosetta.im.client.tags;
|
||||
|
||||
import io.orprotocol.client.ECITag;
|
||||
|
||||
public class ECIDevice extends ECITag {
|
||||
public class ECIDevice implements ECITag {
|
||||
|
||||
public String deviceId;
|
||||
public String deviceName;
|
||||
|
||||
@@ -67,6 +67,36 @@ public abstract class Repository<T> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск сущности по значению одного поля с использованием оператора LIKE
|
||||
* @param fieldName поле
|
||||
* @param value значение
|
||||
* @return найденная сущность или null
|
||||
*/
|
||||
public T likeSearch(String fieldName, String value) {
|
||||
return executeInSession(session -> {
|
||||
String queryString = "FROM " + entityClass.getSimpleName() + " WHERE " + fieldName + " LIKE :value";
|
||||
return session.createQuery(queryString, entityClass)
|
||||
.setParameter("value", "%" + value + "%")
|
||||
.uniqueResult();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск сущности по значению одного поля с использованием оператора LIKE
|
||||
* @param fieldName поле
|
||||
* @param value значение
|
||||
* @return найденная сущность или null
|
||||
*/
|
||||
public List<T> likeSearchAll(String fieldName, String value) {
|
||||
return executeInSession(session -> {
|
||||
String queryString = "FROM " + entityClass.getSimpleName() + " WHERE " + fieldName + " LIKE :value";
|
||||
return session.createQuery(queryString, entityClass)
|
||||
.setParameter("value", "%" + value + "%")
|
||||
.list();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск сущности по набору полей
|
||||
* @param fields карта полей и их значений
|
||||
|
||||
@@ -13,16 +13,15 @@ import com.rosetta.im.event.events.handshake.HandshakeCompletedEvent;
|
||||
import com.rosetta.im.event.events.handshake.HandshakeDeviceConfirmEvent;
|
||||
import com.rosetta.im.event.events.handshake.HandshakeFailedEvent;
|
||||
import com.rosetta.im.packet.Packet0Handshake;
|
||||
import com.rosetta.im.packet.enums.HandshakeStage;
|
||||
import com.rosetta.im.packet.runtime.HandshakeStage;
|
||||
import com.rosetta.im.service.services.DeviceService;
|
||||
|
||||
import io.orprotocol.ProtocolException;
|
||||
import io.orprotocol.client.Client;
|
||||
import io.orprotocol.lock.Lock;
|
||||
import io.orprotocol.packet.Packet;
|
||||
import io.orprotocol.packet.PacketExecutor;
|
||||
|
||||
public class Executor0Handshake extends PacketExecutor {
|
||||
public class Executor0Handshake extends PacketExecutor<Packet0Handshake> {
|
||||
|
||||
private final UserRepository userRepository = new UserRepository();
|
||||
private final DeviceRepository deviceRepository = new DeviceRepository();
|
||||
@@ -36,8 +35,7 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
|
||||
@Override
|
||||
@Lock(lockFor = "publicKey")
|
||||
public void onPacketReceived(Packet packet, Client client) throws ProtocolException {
|
||||
Packet0Handshake handshake = (Packet0Handshake) packet;
|
||||
public void onPacketReceived(Packet0Handshake handshake, Client client) throws ProtocolException {
|
||||
String publicKey = handshake.getPublicKey();
|
||||
String privateKey = handshake.getPrivateKey();
|
||||
String deviceId = handshake.getDeviceId();
|
||||
@@ -67,7 +65,7 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
* Создаем минимальную информацию об устройстве клиента
|
||||
*/
|
||||
ECIDevice device = new ECIDevice(deviceId, deviceName, deviceOs);
|
||||
client.addTag(device);
|
||||
client.addTag(ECIDevice.class, device);
|
||||
|
||||
/**
|
||||
* Проверяем есть ли такой пользователь
|
||||
@@ -95,7 +93,7 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
*/
|
||||
ECIAuthentificate eciTag = new ECIAuthentificate
|
||||
(publicKey, privateKey, HandshakeStage.COMPLETED);
|
||||
client.addTag(eciTag);
|
||||
client.addTag(ECIAuthentificate.class, eciTag);
|
||||
/**
|
||||
* Вызываем событие завершения хэндшейка
|
||||
*/
|
||||
@@ -151,7 +149,7 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
*/
|
||||
ECIAuthentificate eciTag = new ECIAuthentificate
|
||||
(publicKey, privateKey, HandshakeStage.NEED_DEVICE_VERIFICATION);
|
||||
client.addTag(eciTag);
|
||||
client.addTag(ECIAuthentificate.class, eciTag);
|
||||
/**
|
||||
* Отправляем клиенту информацию о необходимости
|
||||
* подтверждения устройства
|
||||
@@ -179,7 +177,7 @@ public class Executor0Handshake extends PacketExecutor {
|
||||
*/
|
||||
ECIAuthentificate eciTag = new ECIAuthentificate
|
||||
(publicKey, privateKey, HandshakeStage.COMPLETED);
|
||||
client.addTag(eciTag);
|
||||
client.addTag(ECIAuthentificate.class, eciTag);
|
||||
/**
|
||||
* Вызываем событие завершения хэндшейка
|
||||
*/
|
||||
|
||||
32
src/main/java/com/rosetta/im/executors/Executor3Search.java
Normal file
32
src/main/java/com/rosetta/im/executors/Executor3Search.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.rosetta.im.executors;
|
||||
|
||||
import com.rosetta.im.Failures;
|
||||
import com.rosetta.im.client.tags.ECIAuthentificate;
|
||||
import com.rosetta.im.database.repository.UserRepository;
|
||||
import com.rosetta.im.packet.Packet3Search;
|
||||
|
||||
import io.orprotocol.ProtocolException;
|
||||
import io.orprotocol.client.Client;
|
||||
import io.orprotocol.packet.PacketExecutor;
|
||||
|
||||
public class Executor3Search extends PacketExecutor<Packet3Search> {
|
||||
|
||||
private final UserRepository userRepository = new UserRepository();
|
||||
|
||||
@Override
|
||||
public void onPacketReceived(Packet3Search packet, Client client) throws Exception, ProtocolException {
|
||||
String search = packet.getSearch();
|
||||
|
||||
ECIAuthentificate eciAuthentificate = client.getTag(ECIAuthentificate.class);
|
||||
if(eciAuthentificate == null || !eciAuthentificate.hasAuthorized()) {
|
||||
/**
|
||||
* Клиент не авторизован, не разрешаем ему выполнять поиск
|
||||
*/
|
||||
client.disconnect(Failures.HANDSHAKE_NOT_COMPLETED);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.rosetta.im.listeners;
|
||||
|
||||
import com.rosetta.im.event.Listener;
|
||||
import com.rosetta.im.event.events.handshake.HandshakeCompletedEvent;
|
||||
|
||||
public class HandshakeCompleteListener implements Listener {
|
||||
|
||||
public void onHandshakeComplete(HandshakeCompletedEvent event) {
|
||||
//TODO: Обработка завершения Handshake и синхронизация недоставленных сообщений (переписок)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.rosetta.im.packet;
|
||||
|
||||
import com.rosetta.im.packet.enums.HandshakeStage;
|
||||
import com.rosetta.im.packet.runtime.HandshakeStage;
|
||||
|
||||
import io.orprotocol.Stream;
|
||||
import io.orprotocol.packet.Packet;
|
||||
|
||||
85
src/main/java/com/rosetta/im/packet/Packet1UserInfo.java
Normal file
85
src/main/java/com/rosetta/im/packet/Packet1UserInfo.java
Normal file
@@ -0,0 +1,85 @@
|
||||
package com.rosetta.im.packet;
|
||||
|
||||
import io.orprotocol.Stream;
|
||||
import io.orprotocol.packet.Packet;
|
||||
|
||||
public class Packet1UserInfo extends Packet {
|
||||
|
||||
@Deprecated(since = "1.1", forRemoval = true)
|
||||
private String privateKey;
|
||||
|
||||
private String username;
|
||||
private String title;
|
||||
|
||||
@Override
|
||||
public void read(Stream stream) {
|
||||
this.packetId = stream.readInt16();
|
||||
this.username = stream.readString();
|
||||
this.title = stream.readString();
|
||||
this.privateKey = stream.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream write() {
|
||||
Stream steram = new Stream();
|
||||
steram.writeInt16(this.packetId);
|
||||
steram.writeString(this.username);
|
||||
steram.writeString(this.title);
|
||||
steram.writeString(this.privateKey);
|
||||
return steram;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Получает приватный ключ пользователя
|
||||
* @return приватный ключ
|
||||
* @deprecated с версии сервера 1.1 использование приватных ключей
|
||||
* в протоколе устарело, так как теперь сервер использует Handshake для аутентификации пользователей.
|
||||
*/
|
||||
@Deprecated(since = "1.1", forRemoval = true)
|
||||
public String getPrivateKey() {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает приватный ключ пользователя
|
||||
* @param privateKey приватный ключ
|
||||
* @deprecated с версии сервера 1.1 использование приватных ключей
|
||||
* в протоколе устарело, так как теперь сервер использует Handshake для аутентификации пользователей.
|
||||
*/
|
||||
public void setPrivateKey(String privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает имя пользователя
|
||||
* @return имя пользователя
|
||||
*/
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает заголовок (титул) пользователя
|
||||
* @return заголовок пользователя
|
||||
*/
|
||||
public String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает имя пользователя
|
||||
* @param username имя пользователя
|
||||
*/
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает заголовок (титул) пользователя
|
||||
* @param title заголовок пользователя
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
40
src/main/java/com/rosetta/im/packet/Packet2Result.java
Normal file
40
src/main/java/com/rosetta/im/packet/Packet2Result.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.rosetta.im.packet;
|
||||
|
||||
import io.orprotocol.Stream;
|
||||
import io.orprotocol.packet.Packet;
|
||||
|
||||
public class Packet2Result extends Packet {
|
||||
|
||||
private int resultCode;
|
||||
|
||||
@Override
|
||||
public void read(Stream stream) {
|
||||
this.resultCode = stream.readInt8();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream write() {
|
||||
Stream stream = new Stream();
|
||||
stream.writeInt16(this.packetId);
|
||||
stream.writeInt8(this.resultCode);
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает код результата операции
|
||||
* @return код результата
|
||||
*/
|
||||
public int getResultCode() {
|
||||
return this.resultCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает код результата операции
|
||||
* @param resultCode код результата
|
||||
*/
|
||||
public void setResultCode(int resultCode) {
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
81
src/main/java/com/rosetta/im/packet/Packet3Search.java
Normal file
81
src/main/java/com/rosetta/im/packet/Packet3Search.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package com.rosetta.im.packet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.rosetta.im.packet.runtime.SearchInfo;
|
||||
|
||||
import io.orprotocol.Stream;
|
||||
import io.orprotocol.packet.Packet;
|
||||
|
||||
public class Packet3Search extends Packet {
|
||||
|
||||
@Deprecated(since = "1.1", forRemoval = true)
|
||||
private String privateKey;
|
||||
private String search;
|
||||
private List<SearchInfo> searchInfo;
|
||||
|
||||
@Override
|
||||
public void read(Stream stream) {
|
||||
this.privateKey = stream.readString();
|
||||
this.search = stream.readString();
|
||||
int searchInfoCount = stream.readInt16();
|
||||
this.searchInfo = new ArrayList<>();
|
||||
for (int i = 0; i < searchInfoCount; i++) {
|
||||
SearchInfo info = new SearchInfo();
|
||||
info.readFromStream(stream);
|
||||
this.searchInfo.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream write() {
|
||||
Stream stream = new Stream();
|
||||
stream.writeInt16(this.packetId);
|
||||
stream.writeString(this.privateKey);
|
||||
stream.writeString(this.search);
|
||||
stream.writeInt16(this.searchInfo.size());
|
||||
for (SearchInfo info : this.searchInfo) {
|
||||
info.writeToStream(stream);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает приватный ключ пользователя
|
||||
* @return приватный ключ
|
||||
* @deprecated с версии сервера 1.1 использование приватных ключей
|
||||
* в протоколе устарело, так как теперь сервер использует Handshake для аутентификации пользователей.
|
||||
*/
|
||||
@Deprecated(since = "1.1", forRemoval = true)
|
||||
public String getPrivateKey() {
|
||||
return this.privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает приватный ключ пользователя
|
||||
* @param privateKey приватный ключ
|
||||
* @deprecated с версии сервера 1.1 использование приватных ключей
|
||||
* в протоколе устарело, так как теперь сервер использует Handshake для аутентификации пользователей.
|
||||
*/
|
||||
public void setPrivateKey(String privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает строку поиска
|
||||
* @return строка поиска
|
||||
*/
|
||||
public String getSearch() {
|
||||
return this.search;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает строку поиска
|
||||
* @param search строка поиска
|
||||
*/
|
||||
public void setSearch(String search) {
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.rosetta.im.packet.enums;
|
||||
package com.rosetta.im.packet.runtime;
|
||||
|
||||
/**
|
||||
* Этапы хэндшейка между клиентом и сервером.
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.rosetta.im.packet.runtime;
|
||||
|
||||
public enum NetworkStatus {
|
||||
ONLINE(0),
|
||||
OFFILE(1);
|
||||
|
||||
private final int code;
|
||||
|
||||
NetworkStatus(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static NetworkStatus fromCode(int code) {
|
||||
for (NetworkStatus status : NetworkStatus.values()) {
|
||||
if (status.getCode() == code) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid NetworkStatus code: " + code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.rosetta.im.service.services;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.rosetta.im.database.entity.User;
|
||||
import com.rosetta.im.database.repository.UserRepository;
|
||||
import com.rosetta.im.service.Service;
|
||||
|
||||
public class UserService extends Service<UserRepository> {
|
||||
|
||||
public UserService(UserRepository repository) {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск пользователей по части имени пользователя.
|
||||
* @param query часть имени пользователя
|
||||
* @return список пользователей, соответствующих запросу
|
||||
*/
|
||||
public List<User> searchUsers(String query) {
|
||||
return getRepository().likeSearchAll("username", query);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user