Implemented storing user messages in collections, improved network organization.
This commit is contained in:
parent
cc5c90fd54
commit
fcfea0f70f
|
@ -11,12 +11,8 @@ import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import nl.andrewl.concord_client.gui.MainWindow;
|
import nl.andrewl.concord_client.gui.MainWindow;
|
||||||
import nl.andrewl.concord_core.msg.Message;
|
import nl.andrewl.concord_core.msg.Message;
|
||||||
import nl.andrewl.concord_core.msg.MessageUtils;
|
|
||||||
import nl.andrewl.concord_core.msg.Serializer;
|
import nl.andrewl.concord_core.msg.Serializer;
|
||||||
import nl.andrewl.concord_core.msg.types.Chat;
|
import nl.andrewl.concord_core.msg.types.*;
|
||||||
import nl.andrewl.concord_core.msg.types.Identification;
|
|
||||||
import nl.andrewl.concord_core.msg.types.ServerMetaData;
|
|
||||||
import nl.andrewl.concord_core.msg.types.ServerWelcome;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
|
@ -28,7 +24,6 @@ import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ConcordClient implements Runnable {
|
public class ConcordClient implements Runnable {
|
||||||
|
|
||||||
private final Socket socket;
|
private final Socket socket;
|
||||||
private final DataInputStream in;
|
private final DataInputStream in;
|
||||||
private final DataOutputStream out;
|
private final DataOutputStream out;
|
||||||
|
@ -53,6 +48,10 @@ public class ConcordClient implements Runnable {
|
||||||
this.id = welcome.getClientId();
|
this.id = welcome.getClientId();
|
||||||
this.currentChannelId = welcome.getCurrentChannelId();
|
this.currentChannelId = welcome.getCurrentChannelId();
|
||||||
this.serverMetaData = welcome.getMetaData();
|
this.serverMetaData = welcome.getMetaData();
|
||||||
|
|
||||||
|
// Start fetching initial data for the channel we were initially put into.
|
||||||
|
this.sendMessage(new ChannelUsersRequest(this.currentChannelId));
|
||||||
|
this.sendMessage(new ChatHistoryRequest(this.currentChannelId, ChatHistoryRequest.Source.CHANNEL, ""));
|
||||||
} else {
|
} else {
|
||||||
throw new IOException("Unexpected response from the server after sending identification message.");
|
throw new IOException("Unexpected response from the server after sending identification message.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,17 @@ import nl.andrewl.concord_client.ConcordClient;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This panel occupies the center of the interface, and displays the list of
|
||||||
|
* recent messages, along with an input text box for the user to type messages
|
||||||
|
* into.
|
||||||
|
*/
|
||||||
public class ChannelChatBox extends Panel {
|
public class ChannelChatBox extends Panel {
|
||||||
private final ConcordClient client;
|
private final ConcordClient client;
|
||||||
private Border chatBorder;
|
private Border chatBorder;
|
||||||
@Getter
|
@Getter
|
||||||
private final ChatList chatList;
|
private final ChatList chatList;
|
||||||
|
@Getter
|
||||||
private final TextBox inputTextBox;
|
private final TextBox inputTextBox;
|
||||||
public ChannelChatBox(ConcordClient client, Window window) {
|
public ChannelChatBox(ConcordClient client, Window window) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
package nl.andrewl.concord_client.gui;
|
package nl.andrewl.concord_client.gui;
|
||||||
|
|
||||||
import com.googlecode.lanterna.gui2.Button;
|
import com.googlecode.lanterna.gui2.*;
|
||||||
import com.googlecode.lanterna.gui2.Direction;
|
|
||||||
import com.googlecode.lanterna.gui2.LinearLayout;
|
|
||||||
import com.googlecode.lanterna.gui2.Panel;
|
|
||||||
import nl.andrewl.concord_client.ConcordClient;
|
import nl.andrewl.concord_client.ConcordClient;
|
||||||
import nl.andrewl.concord_core.msg.types.MoveToChannel;
|
import nl.andrewl.concord_core.msg.types.MoveToChannel;
|
||||||
|
|
||||||
|
@ -11,7 +8,6 @@ import java.io.IOException;
|
||||||
|
|
||||||
public class ChannelList extends Panel {
|
public class ChannelList extends Panel {
|
||||||
private final ConcordClient client;
|
private final ConcordClient client;
|
||||||
|
|
||||||
public ChannelList(ConcordClient client) {
|
public ChannelList(ConcordClient client) {
|
||||||
super(new LinearLayout(Direction.VERTICAL));
|
super(new LinearLayout(Direction.VERTICAL));
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
@ -25,12 +21,13 @@ public class ChannelList extends Panel {
|
||||||
name = "*" + name;
|
name = "*" + name;
|
||||||
}
|
}
|
||||||
Button b = new Button(name, () -> {
|
Button b = new Button(name, () -> {
|
||||||
System.out.println("Sending request to go to channel " + channel.getName());
|
if (!client.getCurrentChannelId().equals(channel.getId())) {
|
||||||
try {
|
try {
|
||||||
client.sendMessage(new MoveToChannel(channel.getId()));
|
client.sendMessage(new MoveToChannel(channel.getId()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.addComponent(b, LinearLayout.createLayoutData(LinearLayout.Alignment.End));
|
this.addComponent(b, LinearLayout.createLayoutData(LinearLayout.Alignment.End));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,7 @@ import lombok.Getter;
|
||||||
import nl.andrewl.concord_client.ClientMessageListener;
|
import nl.andrewl.concord_client.ClientMessageListener;
|
||||||
import nl.andrewl.concord_client.ConcordClient;
|
import nl.andrewl.concord_client.ConcordClient;
|
||||||
import nl.andrewl.concord_core.msg.Message;
|
import nl.andrewl.concord_core.msg.Message;
|
||||||
import nl.andrewl.concord_core.msg.types.Chat;
|
import nl.andrewl.concord_core.msg.types.*;
|
||||||
import nl.andrewl.concord_core.msg.types.MoveToChannel;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -24,16 +23,16 @@ public class ChatPanel extends Panel implements ClientMessageListener {
|
||||||
private final UserList userList;
|
private final UserList userList;
|
||||||
|
|
||||||
private final ConcordClient client;
|
private final ConcordClient client;
|
||||||
|
private final TextGUIThread guiThread;
|
||||||
|
|
||||||
public ChatPanel(ConcordClient client, Window window) {
|
public ChatPanel(ConcordClient client, Window window) {
|
||||||
super(new BorderLayout());
|
super(new BorderLayout());
|
||||||
|
this.guiThread = window.getTextGUI().getGUIThread();
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.channelChatBox = new ChannelChatBox(client, window);
|
this.channelChatBox = new ChannelChatBox(client, window);
|
||||||
this.channelList = new ChannelList(client);
|
this.channelList = new ChannelList(client);
|
||||||
this.channelList.setChannels();
|
this.channelList.setChannels();
|
||||||
this.userList = new UserList();
|
this.userList = new UserList(client);
|
||||||
this.userList.addItem("andrew");
|
|
||||||
this.userList.addItem("tester");
|
|
||||||
|
|
||||||
Border b;
|
Border b;
|
||||||
b = Borders.doubleLine("Channels");
|
b = Borders.doubleLine("Channels");
|
||||||
|
@ -53,9 +52,25 @@ public class ChatPanel extends Panel implements ClientMessageListener {
|
||||||
this.channelChatBox.getChatList().addItem(chat);
|
this.channelChatBox.getChatList().addItem(chat);
|
||||||
} else if (message instanceof MoveToChannel moveToChannel) {
|
} else if (message instanceof MoveToChannel moveToChannel) {
|
||||||
client.setCurrentChannelId(moveToChannel.getChannelId());
|
client.setCurrentChannelId(moveToChannel.getChannelId());
|
||||||
|
try {
|
||||||
|
client.sendMessage(new ChatHistoryRequest(moveToChannel.getChannelId(), ChatHistoryRequest.Source.CHANNEL, ""));
|
||||||
|
client.sendMessage(new ChannelUsersRequest(moveToChannel.getChannelId()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
this.guiThread.invokeLater(() -> {
|
||||||
this.channelList.setChannels();
|
this.channelList.setChannels();
|
||||||
this.channelChatBox.getChatList().clearItems();
|
this.channelChatBox.getChatList().clearItems();
|
||||||
this.channelChatBox.refreshBorder();
|
this.channelChatBox.refreshBorder();
|
||||||
|
this.channelChatBox.getInputTextBox().takeFocus();
|
||||||
|
});
|
||||||
|
} else if (message instanceof ChannelUsersResponse channelUsersResponse) {
|
||||||
|
this.guiThread.invokeLater(() -> {
|
||||||
|
this.userList.updateUsers(channelUsersResponse);
|
||||||
|
});
|
||||||
|
} else if (message instanceof ChatHistoryResponse chatHistoryResponse) {
|
||||||
|
System.out.println("Got chat history response: " + chatHistoryResponse.getSourceId());
|
||||||
|
System.out.println(chatHistoryResponse.getMessages());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,27 @@
|
||||||
package nl.andrewl.concord_client.gui;
|
package nl.andrewl.concord_client.gui;
|
||||||
|
|
||||||
import com.googlecode.lanterna.gui2.AbstractListBox;
|
import com.googlecode.lanterna.gui2.Button;
|
||||||
|
import com.googlecode.lanterna.gui2.Direction;
|
||||||
|
import com.googlecode.lanterna.gui2.LinearLayout;
|
||||||
|
import com.googlecode.lanterna.gui2.Panel;
|
||||||
|
import nl.andrewl.concord_client.ConcordClient;
|
||||||
|
import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
|
||||||
|
|
||||||
public class UserList extends AbstractListBox<String, UserList> {
|
public class UserList extends Panel {
|
||||||
|
private final ConcordClient client;
|
||||||
|
|
||||||
|
public UserList(ConcordClient client) {
|
||||||
|
super(new LinearLayout(Direction.VERTICAL));
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUsers(ChannelUsersResponse usersResponse) {
|
||||||
|
this.removeAllComponents();
|
||||||
|
for (var user : usersResponse.getUsers()) {
|
||||||
|
Button b = new Button(user.getName(), () -> {
|
||||||
|
System.out.println("Opening DM channel with user " + user.getName() + ", id: " + user.getId());
|
||||||
|
});
|
||||||
|
this.addComponent(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ public class Serializer {
|
||||||
registerType(3, MoveToChannel.class);
|
registerType(3, MoveToChannel.class);
|
||||||
registerType(4, ChatHistoryRequest.class);
|
registerType(4, ChatHistoryRequest.class);
|
||||||
registerType(5, ChatHistoryResponse.class);
|
registerType(5, ChatHistoryResponse.class);
|
||||||
|
registerType(6, ChannelUsersRequest.class);
|
||||||
|
registerType(7, ChannelUsersResponse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerType(int id, Class<? extends Message> clazz) {
|
private static void registerType(int id, Class<? extends Message> clazz) {
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package nl.andrewl.concord_core.msg.types;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import nl.andrewl.concord_core.msg.Message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static nl.andrewl.concord_core.msg.MessageUtils.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ChannelUsersRequest implements Message {
|
||||||
|
private UUID channelId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getByteCount() {
|
||||||
|
return UUID_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(DataOutputStream o) throws IOException {
|
||||||
|
writeUUID(this.channelId, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(DataInputStream i) throws IOException {
|
||||||
|
this.channelId = readUUID(i);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package nl.andrewl.concord_core.msg.types;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import nl.andrewl.concord_core.msg.Message;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static nl.andrewl.concord_core.msg.MessageUtils.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ChannelUsersResponse implements Message {
|
||||||
|
private List<UserData> users;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getByteCount() {
|
||||||
|
return getByteSize(this.users);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(DataOutputStream o) throws IOException {
|
||||||
|
writeList(this.users, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(DataInputStream i) throws IOException {
|
||||||
|
try {
|
||||||
|
this.users = readList(UserData.class, i);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class UserData implements Message {
|
||||||
|
private UUID id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getByteCount() {
|
||||||
|
return UUID_BYTES + getByteSize(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(DataOutputStream o) throws IOException {
|
||||||
|
writeUUID(this.id, o);
|
||||||
|
writeString(this.name, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(DataInputStream i) throws IOException {
|
||||||
|
this.id = readUUID(i);
|
||||||
|
this.name = readString(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package nl.andrewl.concord_core.msg.types;
|
package nl.andrewl.concord_core.msg.types;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import nl.andrewl.concord_core.msg.Message;
|
import nl.andrewl.concord_core.msg.Message;
|
||||||
|
@ -46,6 +47,7 @@ import static nl.andrewl.concord_core.msg.MessageUtils.*;
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class ChatHistoryRequest implements Message {
|
public class ChatHistoryRequest implements Message {
|
||||||
public enum Source {CHANNEL, THREAD, DIRECT_MESSAGE}
|
public enum Source {CHANNEL, THREAD, DIRECT_MESSAGE}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package nl.andrewl.concord_core.msg.types;
|
package nl.andrewl.concord_core.msg.types;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import nl.andrewl.concord_core.msg.Message;
|
import nl.andrewl.concord_core.msg.Message;
|
||||||
|
@ -17,6 +18,7 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class ChatHistoryResponse implements Message {
|
public class ChatHistoryResponse implements Message {
|
||||||
private UUID sourceId;
|
private UUID sourceId;
|
||||||
private ChatHistoryRequest.Source sourceType;
|
private ChatHistoryRequest.Source sourceType;
|
||||||
|
|
|
@ -24,5 +24,24 @@
|
||||||
<version>3.4.3</version>
|
<version>3.4.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.12.4</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-core</artifactId>
|
||||||
|
<version>2.12.4</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
<version>2.12.4</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
|
@ -1,9 +1,14 @@
|
||||||
module concord_server {
|
module concord_server {
|
||||||
requires nitrite;
|
requires nitrite;
|
||||||
requires static lombok;
|
requires static lombok;
|
||||||
|
requires com.fasterxml.jackson.databind;
|
||||||
|
requires com.fasterxml.jackson.core;
|
||||||
|
requires com.fasterxml.jackson.annotation;
|
||||||
|
|
||||||
requires java.base;
|
requires java.base;
|
||||||
requires java.logging;
|
requires java.logging;
|
||||||
|
|
||||||
requires concord_core;
|
requires concord_core;
|
||||||
|
|
||||||
|
opens nl.andrewl.concord_server.config to com.fasterxml.jackson.databind;
|
||||||
}
|
}
|
|
@ -3,12 +3,12 @@ package nl.andrewl.concord_server;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import nl.andrewl.concord_core.msg.Message;
|
import nl.andrewl.concord_core.msg.Message;
|
||||||
import nl.andrewl.concord_core.msg.Serializer;
|
import nl.andrewl.concord_core.msg.Serializer;
|
||||||
|
import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
|
||||||
|
import org.dizitart.no2.NitriteCollection;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.*;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,19 +23,32 @@ public class Channel {
|
||||||
|
|
||||||
private final Set<ClientThread> connectedClients;
|
private final Set<ClientThread> connectedClients;
|
||||||
|
|
||||||
public Channel(ConcordServer server, UUID id, String name) {
|
private final NitriteCollection messageCollection;
|
||||||
|
|
||||||
|
public Channel(ConcordServer server, UUID id, String name, NitriteCollection messageCollection) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.connectedClients = ConcurrentHashMap.newKeySet();
|
this.connectedClients = ConcurrentHashMap.newKeySet();
|
||||||
|
this.messageCollection = messageCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addClient(ClientThread clientThread) {
|
public void addClient(ClientThread clientThread) {
|
||||||
this.connectedClients.add(clientThread);
|
this.connectedClients.add(clientThread);
|
||||||
|
try {
|
||||||
|
this.sendMessage(new ChannelUsersResponse(this.getUserData()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeClient(ClientThread clientThread) {
|
public void removeClient(ClientThread clientThread) {
|
||||||
this.connectedClients.remove(clientThread);
|
this.connectedClients.remove(clientThread);
|
||||||
|
try {
|
||||||
|
this.sendMessage(new ChannelUsersResponse(this.getUserData()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,6 +66,15 @@ public class Channel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ChannelUsersResponse.UserData> getUserData() {
|
||||||
|
List<ChannelUsersResponse.UserData> users = new ArrayList<>();
|
||||||
|
for (var clientThread : this.getConnectedClients()) {
|
||||||
|
users.add(new ChannelUsersResponse.UserData(clientThread.getClientId(), clientThread.getClientNickname()));
|
||||||
|
}
|
||||||
|
users.sort(Comparator.comparing(ChannelUsersResponse.UserData::getName));
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|
|
@ -2,7 +2,10 @@ package nl.andrewl.concord_server;
|
||||||
|
|
||||||
import nl.andrewl.concord_core.msg.types.MoveToChannel;
|
import nl.andrewl.concord_core.msg.types.MoveToChannel;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class ChannelManager {
|
public class ChannelManager {
|
||||||
|
@ -14,10 +17,6 @@ public class ChannelManager {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.channelNameMap = new ConcurrentHashMap<>();
|
this.channelNameMap = new ConcurrentHashMap<>();
|
||||||
this.channelIdMap = new ConcurrentHashMap<>();
|
this.channelIdMap = new ConcurrentHashMap<>();
|
||||||
Channel general = new Channel(server, server.getIdProvider().newId(), "general");
|
|
||||||
Channel memes = new Channel(server, server.getIdProvider().newId(), "memes");
|
|
||||||
this.addChannel(general);
|
|
||||||
this.addChannel(memes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Channel> getChannels() {
|
public Set<Channel> getChannels() {
|
||||||
|
@ -44,7 +43,8 @@ public class ChannelManager {
|
||||||
|
|
||||||
public void moveToChannel(ClientThread client, Channel channel) {
|
public void moveToChannel(ClientThread client, Channel channel) {
|
||||||
if (client.getCurrentChannel() != null) {
|
if (client.getCurrentChannel() != null) {
|
||||||
client.getCurrentChannel().removeClient(client);
|
var previousChannel = client.getCurrentChannel();
|
||||||
|
previousChannel.removeClient(client);
|
||||||
}
|
}
|
||||||
channel.addClient(client);
|
channel.addClient(client);
|
||||||
client.setCurrentChannel(channel);
|
client.setCurrentChannel(channel);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import lombok.extern.java.Log;
|
||||||
import nl.andrewl.concord_core.msg.Message;
|
import nl.andrewl.concord_core.msg.Message;
|
||||||
import nl.andrewl.concord_core.msg.Serializer;
|
import nl.andrewl.concord_core.msg.Serializer;
|
||||||
import nl.andrewl.concord_core.msg.types.Identification;
|
import nl.andrewl.concord_core.msg.types.Identification;
|
||||||
import nl.andrewl.concord_core.msg.types.ServerWelcome;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
|
@ -16,7 +15,7 @@ import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This thread is responsible for handling the connection to a single client of
|
* This thread is responsible for handling the connection to a single client of
|
||||||
* a server.
|
* a server. The client thread acts as the server's representation of a client.
|
||||||
*/
|
*/
|
||||||
@Log
|
@Log
|
||||||
public class ClientThread extends Thread {
|
public class ClientThread extends Thread {
|
||||||
|
@ -26,8 +25,11 @@ public class ClientThread extends Thread {
|
||||||
|
|
||||||
private final ConcordServer server;
|
private final ConcordServer server;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private UUID clientId = null;
|
private UUID clientId = null;
|
||||||
@Getter
|
@Getter
|
||||||
|
@Setter
|
||||||
private String clientNickname = null;
|
private String clientNickname = null;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -43,7 +45,7 @@ public class ClientThread extends Thread {
|
||||||
this.out = new DataOutputStream(socket.getOutputStream());
|
this.out = new DataOutputStream(socket.getOutputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendToClient(Message message) {
|
public synchronized void sendToClient(Message message) {
|
||||||
try {
|
try {
|
||||||
Serializer.writeMessage(message, this.out);
|
Serializer.writeMessage(message, this.out);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -51,7 +53,7 @@ public class ClientThread extends Thread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendToClient(byte[] bytes) {
|
public synchronized void sendToClient(byte[] bytes) {
|
||||||
try {
|
try {
|
||||||
this.out.write(bytes);
|
this.out.write(bytes);
|
||||||
this.out.flush();
|
this.out.flush();
|
||||||
|
@ -112,8 +114,7 @@ public class ClientThread extends Thread {
|
||||||
try {
|
try {
|
||||||
var msg = Serializer.readMessage(this.in);
|
var msg = Serializer.readMessage(this.in);
|
||||||
if (msg instanceof Identification id) {
|
if (msg instanceof Identification id) {
|
||||||
this.clientNickname = id.getNickname();
|
this.server.registerClient(id, this);
|
||||||
this.clientId = this.server.registerClient(id, this);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
|
@ -5,11 +5,15 @@ import lombok.extern.java.Log;
|
||||||
import nl.andrewl.concord_core.msg.types.Identification;
|
import nl.andrewl.concord_core.msg.types.Identification;
|
||||||
import nl.andrewl.concord_core.msg.types.ServerMetaData;
|
import nl.andrewl.concord_core.msg.types.ServerMetaData;
|
||||||
import nl.andrewl.concord_core.msg.types.ServerWelcome;
|
import nl.andrewl.concord_core.msg.types.ServerWelcome;
|
||||||
|
import nl.andrewl.concord_server.config.ServerConfig;
|
||||||
|
import org.dizitart.no2.IndexOptions;
|
||||||
|
import org.dizitart.no2.IndexType;
|
||||||
import org.dizitart.no2.Nitrite;
|
import org.dizitart.no2.Nitrite;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -22,6 +26,7 @@ import java.util.stream.Collectors;
|
||||||
public class ConcordServer implements Runnable {
|
public class ConcordServer implements Runnable {
|
||||||
private final Map<UUID, ClientThread> clients;
|
private final Map<UUID, ClientThread> clients;
|
||||||
private final int port;
|
private final int port;
|
||||||
|
private final String name;
|
||||||
@Getter
|
@Getter
|
||||||
private final IdProvider idProvider;
|
private final IdProvider idProvider;
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -34,9 +39,11 @@ public class ConcordServer implements Runnable {
|
||||||
@Getter
|
@Getter
|
||||||
private final ChannelManager channelManager;
|
private final ChannelManager channelManager;
|
||||||
|
|
||||||
public ConcordServer(int port) {
|
public ConcordServer() {
|
||||||
this.port = port;
|
|
||||||
this.idProvider = new UUIDProvider();
|
this.idProvider = new UUIDProvider();
|
||||||
|
ServerConfig config = ServerConfig.loadOrCreate(Path.of("server-config.json"), idProvider);
|
||||||
|
this.port = config.port();
|
||||||
|
this.name = config.name();
|
||||||
this.db = Nitrite.builder()
|
this.db = Nitrite.builder()
|
||||||
.filePath("concord-server.db")
|
.filePath("concord-server.db")
|
||||||
.openOrCreate();
|
.openOrCreate();
|
||||||
|
@ -45,6 +52,33 @@ public class ConcordServer implements Runnable {
|
||||||
this.executorService = Executors.newCachedThreadPool();
|
this.executorService = Executors.newCachedThreadPool();
|
||||||
this.eventManager = new EventManager(this);
|
this.eventManager = new EventManager(this);
|
||||||
this.channelManager = new ChannelManager(this);
|
this.channelManager = new ChannelManager(this);
|
||||||
|
for (var channelConfig : config.channels()) {
|
||||||
|
this.channelManager.addChannel(new Channel(
|
||||||
|
this,
|
||||||
|
UUID.fromString(channelConfig.id()),
|
||||||
|
channelConfig.name(),
|
||||||
|
this.db.getCollection("channel-" + channelConfig.id())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
this.initDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDatabase() {
|
||||||
|
for (var channel : this.channelManager.getChannels()) {
|
||||||
|
var col = channel.getMessageCollection();
|
||||||
|
if (!col.hasIndex("timestamp")) {
|
||||||
|
log.info("Adding timestamp index to collection for channel " + channel.getName());
|
||||||
|
col.createIndex("timestamp", IndexOptions.indexOptions(IndexType.NonUnique));
|
||||||
|
}
|
||||||
|
if (!col.hasIndex("senderNickname")) {
|
||||||
|
log.info("Adding senderNickname index to collection for channel " + channel.getName());
|
||||||
|
col.createIndex("senderNickname", IndexOptions.indexOptions(IndexType.Fulltext));
|
||||||
|
}
|
||||||
|
if (!col.hasIndex("message")) {
|
||||||
|
log.info("Adding message index to collection for channel " + channel.getName());
|
||||||
|
col.createIndex("message", IndexOptions.indexOptions(IndexType.Fulltext));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,28 +89,34 @@ public class ConcordServer implements Runnable {
|
||||||
* channel.
|
* channel.
|
||||||
* @param identification The client's identification data.
|
* @param identification The client's identification data.
|
||||||
* @param clientThread The client manager thread.
|
* @param clientThread The client manager thread.
|
||||||
* @return The id of the client.
|
|
||||||
*/
|
*/
|
||||||
public UUID registerClient(Identification identification, ClientThread clientThread) {
|
public void registerClient(Identification identification, ClientThread clientThread) {
|
||||||
var id = this.idProvider.newId();
|
var id = this.idProvider.newId();
|
||||||
log.info("Registering new client " + identification.getNickname() + " with id " + id);
|
log.info("Registering new client " + identification.getNickname() + " with id " + id);
|
||||||
this.clients.put(id, clientThread);
|
this.clients.put(id, clientThread);
|
||||||
|
clientThread.setClientId(id);
|
||||||
|
clientThread.setClientNickname(identification.getNickname());
|
||||||
// Send a welcome reply containing all the initial server info the client needs.
|
// Send a welcome reply containing all the initial server info the client needs.
|
||||||
ServerMetaData metaData = new ServerMetaData(
|
ServerMetaData metaData = new ServerMetaData(
|
||||||
"Testing Server",
|
this.name,
|
||||||
this.channelManager.getChannels().stream()
|
this.channelManager.getChannels().stream()
|
||||||
.map(channel -> new ServerMetaData.ChannelData(channel.getId(), channel.getName()))
|
.map(channel -> new ServerMetaData.ChannelData(channel.getId(), channel.getName()))
|
||||||
.sorted(Comparator.comparing(ServerMetaData.ChannelData::getName))
|
.sorted(Comparator.comparing(ServerMetaData.ChannelData::getName))
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
);
|
);
|
||||||
|
// Immediately add the client to the default channel and send the initial welcome message.
|
||||||
var defaultChannel = this.channelManager.getChannelByName("general").orElseThrow();
|
var defaultChannel = this.channelManager.getChannelByName("general").orElseThrow();
|
||||||
|
clientThread.sendToClient(new ServerWelcome(id, defaultChannel.getId(), metaData));
|
||||||
|
// It is important that we send the welcome message first. The client expects this as the initial response to their identification message.
|
||||||
defaultChannel.addClient(clientThread);
|
defaultChannel.addClient(clientThread);
|
||||||
clientThread.setCurrentChannel(defaultChannel);
|
clientThread.setCurrentChannel(defaultChannel);
|
||||||
clientThread.sendToClient(new ServerWelcome(id, defaultChannel.getId(), metaData));
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* De-registers a client from the server, removing them from any channel
|
||||||
|
* they're currently in.
|
||||||
|
* @param clientId The id of the client to remove.
|
||||||
|
*/
|
||||||
public void deregisterClient(UUID clientId) {
|
public void deregisterClient(UUID clientId) {
|
||||||
var client = this.clients.remove(clientId);
|
var client = this.clients.remove(clientId);
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
|
@ -104,7 +144,7 @@ public class ConcordServer implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var server = new ConcordServer(8123);
|
var server = new ConcordServer();
|
||||||
server.run();
|
server.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,19 @@ package nl.andrewl.concord_server;
|
||||||
|
|
||||||
import lombok.extern.java.Log;
|
import lombok.extern.java.Log;
|
||||||
import nl.andrewl.concord_core.msg.Message;
|
import nl.andrewl.concord_core.msg.Message;
|
||||||
|
import nl.andrewl.concord_core.msg.types.ChannelUsersRequest;
|
||||||
import nl.andrewl.concord_core.msg.types.Chat;
|
import nl.andrewl.concord_core.msg.types.Chat;
|
||||||
|
import nl.andrewl.concord_core.msg.types.ChatHistoryRequest;
|
||||||
import nl.andrewl.concord_core.msg.types.MoveToChannel;
|
import nl.andrewl.concord_core.msg.types.MoveToChannel;
|
||||||
import nl.andrewl.concord_server.event.ChannelMoveHandler;
|
import nl.andrewl.concord_server.event.*;
|
||||||
import nl.andrewl.concord_server.event.ChatHandler;
|
|
||||||
import nl.andrewl.concord_server.event.MessageHandler;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The event manager is responsible for the server's ability to respond to
|
||||||
|
* various client requests.
|
||||||
|
*/
|
||||||
@Log
|
@Log
|
||||||
public class EventManager {
|
public class EventManager {
|
||||||
private final Map<Class<? extends Message>, MessageHandler<?>> messageHandlers;
|
private final Map<Class<? extends Message>, MessageHandler<?>> messageHandlers;
|
||||||
|
@ -21,8 +25,25 @@ public class EventManager {
|
||||||
this.messageHandlers = new HashMap<>();
|
this.messageHandlers = new HashMap<>();
|
||||||
this.messageHandlers.put(Chat.class, new ChatHandler());
|
this.messageHandlers.put(Chat.class, new ChatHandler());
|
||||||
this.messageHandlers.put(MoveToChannel.class, new ChannelMoveHandler());
|
this.messageHandlers.put(MoveToChannel.class, new ChannelMoveHandler());
|
||||||
|
this.messageHandlers.put(ChannelUsersRequest.class, new ChannelUsersRequestHandler());
|
||||||
|
this.messageHandlers.put(ChatHistoryRequest.class, new ChatHistoryRequestHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a new message that was sent from a client. Tries to find an
|
||||||
|
* appropriate handler for the message, and if one is found, calls the
|
||||||
|
* {@link MessageHandler#handle(Message, ClientThread, ConcordServer)}
|
||||||
|
* method on it.
|
||||||
|
* <p>
|
||||||
|
* Note that it is expected that client threads will invoke this method
|
||||||
|
* during their {@link ClientThread#run()} method, so concurrent
|
||||||
|
* invocation is expected.
|
||||||
|
* </p>
|
||||||
|
* @param message The message that was sent by a client.
|
||||||
|
* @param client The client thread that is used for communicating with the
|
||||||
|
* client.
|
||||||
|
* @param <T> The type of message.
|
||||||
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T extends Message> void handle(T message, ClientThread client) {
|
public <T extends Message> void handle(T message, ClientThread client) {
|
||||||
MessageHandler<T> handler = (MessageHandler<T>) this.messageHandlers.get(message.getClass());
|
MessageHandler<T> handler = (MessageHandler<T>) this.messageHandlers.get(message.getClass());
|
||||||
|
@ -30,6 +51,7 @@ public class EventManager {
|
||||||
try {
|
try {
|
||||||
handler.handle(message, client, this.server);
|
handler.handle(message, client, this.server);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
log.warning("Exception occurred while handling message: " + e.getMessage());
|
log.warning("Exception occurred while handling message: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package nl.andrewl.concord_server.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.extern.java.Log;
|
||||||
|
import nl.andrewl.concord_server.IdProvider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
@Log
|
||||||
|
public record ServerConfig(
|
||||||
|
String name,
|
||||||
|
int port,
|
||||||
|
ChannelConfig[] channels
|
||||||
|
) {
|
||||||
|
|
||||||
|
public static record ChannelConfig (
|
||||||
|
String id,
|
||||||
|
String name,
|
||||||
|
String description
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static ServerConfig loadOrCreate(Path filePath, IdProvider idProvider) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
ServerConfig config;
|
||||||
|
if (Files.notExists(filePath)) {
|
||||||
|
config = new ServerConfig(
|
||||||
|
"My Concord Server",
|
||||||
|
8123,
|
||||||
|
new ServerConfig.ChannelConfig[]{
|
||||||
|
new ServerConfig.ChannelConfig(idProvider.newId().toString(), "general", "Default channel for general discussion.")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
try (var out = Files.newOutputStream(filePath)) {
|
||||||
|
mapper.writerWithDefaultPrettyPrinter().writeValue(out, config);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
log.info(filePath + " does not exist. Creating it with initial values. Edit and restart to apply changes.");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
config = mapper.readValue(Files.newInputStream(filePath), ServerConfig.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
log.info("Loaded configuration from " + filePath);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package nl.andrewl.concord_server.event;
|
||||||
|
|
||||||
|
import nl.andrewl.concord_core.msg.types.ChannelUsersRequest;
|
||||||
|
import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
|
||||||
|
import nl.andrewl.concord_server.ClientThread;
|
||||||
|
import nl.andrewl.concord_server.ConcordServer;
|
||||||
|
|
||||||
|
public class ChannelUsersRequestHandler implements MessageHandler<ChannelUsersRequest> {
|
||||||
|
@Override
|
||||||
|
public void handle(ChannelUsersRequest msg, ClientThread client, ConcordServer server) throws Exception {
|
||||||
|
var optionalChannel = server.getChannelManager().getChannelById(msg.getChannelId());
|
||||||
|
if (optionalChannel.isPresent()) {
|
||||||
|
var channel = optionalChannel.get();
|
||||||
|
client.sendToClient(new ChannelUsersResponse(channel.getUserData()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,10 +12,8 @@ public class ChatHandler implements MessageHandler<Chat> {
|
||||||
@Override
|
@Override
|
||||||
public void handle(Chat msg, ClientThread client, ConcordServer server) throws IOException {
|
public void handle(Chat msg, ClientThread client, ConcordServer server) throws IOException {
|
||||||
server.getExecutorService().submit(() -> {
|
server.getExecutorService().submit(() -> {
|
||||||
var collection = server.getDb().getCollection("channel-" + client.getCurrentChannel().getId());
|
var collection = client.getCurrentChannel().getMessageCollection();
|
||||||
var messageId = server.getIdProvider().newId();
|
|
||||||
Document doc = new Document(Map.of(
|
Document doc = new Document(Map.of(
|
||||||
"_id", messageId,
|
|
||||||
"senderId", msg.getSenderId(),
|
"senderId", msg.getSenderId(),
|
||||||
"senderNickname", msg.getSenderNickname(),
|
"senderNickname", msg.getSenderNickname(),
|
||||||
"timestamp", msg.getTimestamp(),
|
"timestamp", msg.getTimestamp(),
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package nl.andrewl.concord_server.event;
|
||||||
|
|
||||||
|
import nl.andrewl.concord_core.msg.types.Chat;
|
||||||
|
import nl.andrewl.concord_core.msg.types.ChatHistoryRequest;
|
||||||
|
import nl.andrewl.concord_core.msg.types.ChatHistoryResponse;
|
||||||
|
import nl.andrewl.concord_server.ClientThread;
|
||||||
|
import nl.andrewl.concord_server.ConcordServer;
|
||||||
|
import org.dizitart.no2.Document;
|
||||||
|
import org.dizitart.no2.FindOptions;
|
||||||
|
import org.dizitart.no2.SortOrder;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ChatHistoryRequestHandler implements MessageHandler<ChatHistoryRequest> {
|
||||||
|
@Override
|
||||||
|
public void handle(ChatHistoryRequest msg, ClientThread client, ConcordServer server) throws Exception {
|
||||||
|
var optionalChannel = server.getChannelManager().getChannelById(msg.getSourceId());
|
||||||
|
if (optionalChannel.isPresent()) {
|
||||||
|
var channel = optionalChannel.get();
|
||||||
|
System.out.println("Looking for chats in channel-" + channel.getId());
|
||||||
|
var col = server.getDb().getCollection("channel-" + channel.getId());
|
||||||
|
var cursor = col.find(
|
||||||
|
FindOptions.sort("timestamp", SortOrder.Ascending)
|
||||||
|
.thenLimit(0, 10)
|
||||||
|
);
|
||||||
|
List<Chat> chats = new ArrayList<>(10);
|
||||||
|
for (Document doc : cursor) {
|
||||||
|
chats.add(new Chat(
|
||||||
|
doc.get("senderId", UUID.class),
|
||||||
|
doc.get("senderNickname", String.class),
|
||||||
|
doc.get("timestamp", Long.class),
|
||||||
|
doc.get("message", String.class)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
col.close();
|
||||||
|
System.out.println(chats);
|
||||||
|
client.sendToClient(new ChatHistoryResponse(msg.getSourceId(), msg.getSourceType(), chats));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue