Added catalog discovery server, readmes and improved some server channel logic.
This commit is contained in:
parent
31129cada2
commit
e9fa0c13a7
|
@ -0,0 +1,3 @@
|
||||||
|
# Concord Catalog
|
||||||
|
|
||||||
|
The catalog is an HTTP server that is used as a "discovery" server that connects clients to the concord servers they might want to join. Clients will request a list of servers from the catalog, and servers are responsible for regularly sending their metadata to any catalogs they wish to be publicly visible in.
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>concord</artifactId>
|
||||||
|
<groupId>nl.andrewl</groupId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>concord-catalog</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>16</maven.compiler.source>
|
||||||
|
<maven.compiler.target>16</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.undertow</groupId>
|
||||||
|
<artifactId>undertow-core</artifactId>
|
||||||
|
<version>2.2.8.Final</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.undertow</groupId>
|
||||||
|
<artifactId>undertow-servlet</artifactId>
|
||||||
|
<version>2.2.8.Final</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.12.4</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,10 @@
|
||||||
|
module concord_catalog {
|
||||||
|
requires com.fasterxml.jackson.databind;
|
||||||
|
requires undertow.core;
|
||||||
|
requires undertow.servlet;
|
||||||
|
requires java.servlet;
|
||||||
|
requires jdk.unsupported;
|
||||||
|
|
||||||
|
exports nl.andrewl.concord_catalog.servlet to undertow.servlet;
|
||||||
|
opens nl.andrewl.concord_catalog.servlet to com.fasterxml.jackson.databind;
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package nl.andrewl.concord_catalog;
|
||||||
|
|
||||||
|
import io.undertow.Undertow;
|
||||||
|
import io.undertow.server.HttpHandler;
|
||||||
|
import io.undertow.servlet.Servlets;
|
||||||
|
import io.undertow.servlet.api.DeploymentInfo;
|
||||||
|
import io.undertow.servlet.api.DeploymentManager;
|
||||||
|
import nl.andrewl.concord_catalog.servlet.ServersServlet;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class CatalogServer {
|
||||||
|
private static final String SETTINGS_FILE = "concord-catalog.properties";
|
||||||
|
|
||||||
|
public static void main(String[] args) throws ServletException, IOException {
|
||||||
|
var props = loadProperties();
|
||||||
|
startServer(Integer.parseInt(props.getProperty("port")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the Undertow HTTP servlet container.
|
||||||
|
* @param port The port to bind to.
|
||||||
|
* @throws ServletException If the server could not be started.
|
||||||
|
*/
|
||||||
|
private static void startServer(int port) throws ServletException {
|
||||||
|
System.out.println("Starting server on port " + port + ".");
|
||||||
|
DeploymentInfo servletBuilder = Servlets.deployment()
|
||||||
|
.setClassLoader(CatalogServer.class.getClassLoader())
|
||||||
|
.setContextPath("/")
|
||||||
|
.setDeploymentName("Concord Catalog")
|
||||||
|
.addServlets(
|
||||||
|
Servlets.servlet("ServersServlet", ServersServlet.class)
|
||||||
|
.addMapping("/servers")
|
||||||
|
);
|
||||||
|
DeploymentManager manager = Servlets.defaultContainer().addDeployment(servletBuilder);
|
||||||
|
manager.deploy();
|
||||||
|
HttpHandler servletHandler = manager.start();
|
||||||
|
Undertow server = Undertow.builder()
|
||||||
|
.addHttpListener(port, "0.0.0.0")
|
||||||
|
.setHandler(servletHandler)
|
||||||
|
.build();
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads properties from all necessary locations.
|
||||||
|
* @return The properties that were loaded.
|
||||||
|
* @throws IOException If an error occurs while reading properties.
|
||||||
|
*/
|
||||||
|
private static Properties loadProperties() throws IOException {
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.load(CatalogServer.class.getResourceAsStream("/nl/andrewl/concord_catalog/defaults.properties"));
|
||||||
|
Path settingsPath = Path.of(SETTINGS_FILE);
|
||||||
|
if (Files.exists(settingsPath)) {
|
||||||
|
props.load(Files.newBufferedReader(settingsPath));
|
||||||
|
} else {
|
||||||
|
System.out.println("Using built-in default settings. Create a concord-catalog.properties file to configure.");
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package nl.andrewl.concord_catalog.servlet;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Page<T> {
|
||||||
|
private final List<T> contents;
|
||||||
|
private final int elementCount;
|
||||||
|
private final int pageSize;
|
||||||
|
private final int currentPage;
|
||||||
|
private final boolean firstPage;
|
||||||
|
private final boolean lastPage;
|
||||||
|
private final String order;
|
||||||
|
private final String orderDirection;
|
||||||
|
|
||||||
|
public Page(List<T> contents, int currentPage, int pageSize, String order, String orderDirection) {
|
||||||
|
this.contents = contents;
|
||||||
|
this.elementCount = contents.size();
|
||||||
|
this.pageSize = pageSize;
|
||||||
|
this.currentPage = currentPage;
|
||||||
|
this.firstPage = currentPage == 0;
|
||||||
|
this.lastPage = this.elementCount < this.pageSize;
|
||||||
|
this.order = order;
|
||||||
|
this.orderDirection = orderDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> getContents() {
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getElementCount() {
|
||||||
|
return elementCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPageSize() {
|
||||||
|
return pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentPage() {
|
||||||
|
return currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFirstPage() {
|
||||||
|
return firstPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastPage() {
|
||||||
|
return lastPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrderDirection() {
|
||||||
|
return orderDirection;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package nl.andrewl.concord_catalog.servlet;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ServerMetaData implements Comparable<ServerMetaData> {
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private String host;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
private long lastUpdatedAt;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHost(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return this.host + ":" + this.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastUpdatedAt() {
|
||||||
|
return lastUpdatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastUpdatedAt(long lastUpdatedAt) {
|
||||||
|
this.lastUpdatedAt = lastUpdatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(ServerMetaData o) {
|
||||||
|
int result = this.name.compareTo(o.getName());
|
||||||
|
if (result == 0) {
|
||||||
|
result = this.getAddress().compareTo(o.getAddress());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o.getClass().equals(this.getClass())) {
|
||||||
|
ServerMetaData other = (ServerMetaData) o;
|
||||||
|
return this.name.equals(other.getName()) && this.getAddress().equals(other.getAddress());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(getName(), getAddress());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package nl.andrewl.concord_catalog.servlet;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This servlet is the main HTTP endpoint for getting the list of servers and
|
||||||
|
* also uploading one's own server data.
|
||||||
|
*/
|
||||||
|
public class ServersServlet extends HttpServlet {
|
||||||
|
private static final SortedSet<ServerMetaData> servers = new TreeSet<>();
|
||||||
|
private static final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
static {
|
||||||
|
executorService.scheduleAtFixedRate(() -> {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
servers.removeIf(server -> server.getLastUpdatedAt() < now - (5 * 60000));
|
||||||
|
}, 1, 1, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
resp.setContentType("application/json");
|
||||||
|
mapper.writeValue(resp.getWriter(), servers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||||
|
if (servers.size() > 10000) {
|
||||||
|
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||||
|
resp.setContentType("application/json");
|
||||||
|
mapper.writeValue(resp.getWriter(), Map.of("message", "Too many servers registered at this time."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ServerMetaData data = mapper.readValue(req.getReader(), ServerMetaData.class);
|
||||||
|
data.setHost(req.getRemoteHost());
|
||||||
|
data.setLastUpdatedAt(System.currentTimeMillis());
|
||||||
|
synchronized (servers) {
|
||||||
|
servers.remove(data);
|
||||||
|
servers.add(data);
|
||||||
|
}
|
||||||
|
resp.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
resp.setContentType("application/json");
|
||||||
|
mapper.writeValue(resp.getWriter(), data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
port=25566
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Concord Client
|
||||||
|
|
||||||
|
The concord client is the application which users will run to connect to and interact with various servers. It displays a GUI in the terminal that shows recent chat messages, channels, and the list of users in a channel.
|
|
@ -22,7 +22,7 @@ public class ChatRenderer extends AbstractListBox.ListItemRenderer<Chat, ChatLis
|
||||||
}
|
}
|
||||||
graphics.putString(0, 0, chat.getSenderNickname());
|
graphics.putString(0, 0, chat.getSenderNickname());
|
||||||
Instant timestamp = Instant.ofEpochMilli(chat.getTimestamp());
|
Instant timestamp = Instant.ofEpochMilli(chat.getTimestamp());
|
||||||
String timeStr = timestamp.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("HH:mm:ss"));
|
String timeStr = timestamp.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("HH:mm"));
|
||||||
String label = chat.getSenderNickname() + "@" + timeStr + " : " + chat.getMessage();
|
String label = chat.getSenderNickname() + "@" + timeStr + " : " + chat.getMessage();
|
||||||
label = TerminalTextUtils.fitString(label, graphics.getSize().getColumns());
|
label = TerminalTextUtils.fitString(label, graphics.getSize().getColumns());
|
||||||
while(TerminalTextUtils.getColumnWidth(label) < graphics.getSize().getColumns()) {
|
while(TerminalTextUtils.getColumnWidth(label) < graphics.getSize().getColumns()) {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Concord Core
|
||||||
|
|
||||||
|
This module contains the core resources that both the client and server depend on.
|
1
pom.xml
1
pom.xml
|
@ -12,6 +12,7 @@
|
||||||
<module>server</module>
|
<module>server</module>
|
||||||
<module>core</module>
|
<module>core</module>
|
||||||
<module>client</module>
|
<module>client</module>
|
||||||
|
<module>catalog</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
@ -7,6 +7,7 @@ module concord_server {
|
||||||
|
|
||||||
requires java.base;
|
requires java.base;
|
||||||
requires java.logging;
|
requires java.logging;
|
||||||
|
requires java.net.http;
|
||||||
|
|
||||||
requires concord_core;
|
requires concord_core;
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,17 @@ public class ChannelManager {
|
||||||
return Set.copyOf(this.channelIdMap.values());
|
return Set.copyOf(this.channelIdMap.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<Channel> getDefaultChannel() {
|
||||||
|
var optionalGeneral = this.getChannelByName("general");
|
||||||
|
if (optionalGeneral.isPresent()) {
|
||||||
|
return optionalGeneral;
|
||||||
|
}
|
||||||
|
for (var channel : this.getChannels()) {
|
||||||
|
return Optional.of(channel);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
public void addChannel(Channel channel) {
|
public void addChannel(Channel channel) {
|
||||||
this.channelNameMap.put(channel.getName(), channel);
|
this.channelNameMap.put(channel.getName(), channel);
|
||||||
this.channelIdMap.put(channel.getId(), channel);
|
this.channelIdMap.put(channel.getId(), channel);
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package nl.andrewl.concord_server;
|
package nl.andrewl.concord_server;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
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;
|
||||||
|
@ -12,18 +15,21 @@ import nl.andrewl.concord_server.config.ServerConfig;
|
||||||
import org.dizitart.no2.Nitrite;
|
import org.dizitart.no2.Nitrite;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,6 +52,11 @@ public class ConcordServer implements Runnable {
|
||||||
@Getter
|
@Getter
|
||||||
private final ChannelManager channelManager;
|
private final ChannelManager channelManager;
|
||||||
|
|
||||||
|
// Components for communicating with discovery servers.
|
||||||
|
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
private final HttpClient httpClient = HttpClient.newHttpClient();
|
||||||
|
private final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
public ConcordServer() {
|
public ConcordServer() {
|
||||||
this.idProvider = new UUIDProvider();
|
this.idProvider = new UUIDProvider();
|
||||||
this.config = ServerConfig.loadOrCreate(Path.of("server-config.json"), idProvider);
|
this.config = ServerConfig.loadOrCreate(Path.of("server-config.json"), idProvider);
|
||||||
|
@ -53,7 +64,6 @@ public class ConcordServer implements Runnable {
|
||||||
.filePath("concord-server.db")
|
.filePath("concord-server.db")
|
||||||
.openOrCreate();
|
.openOrCreate();
|
||||||
this.clients = new ConcurrentHashMap<>(32);
|
this.clients = new ConcurrentHashMap<>(32);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -83,7 +93,7 @@ public class ConcordServer implements Runnable {
|
||||||
clientThread.setClientId(id);
|
clientThread.setClientId(id);
|
||||||
clientThread.setClientNickname(identification.getNickname());
|
clientThread.setClientNickname(identification.getNickname());
|
||||||
// Immediately add the client to the default channel and send the initial welcome message.
|
// 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.getDefaultChannel().orElseThrow();
|
||||||
clientThread.sendToClient(new ServerWelcome(id, defaultChannel.getId(), this.getMetaData()));
|
clientThread.sendToClient(new ServerWelcome(id, defaultChannel.getId(), this.getMetaData()));
|
||||||
// It is important that we send the welcome message first. The client expects this as the initial response to their identification message.
|
// 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);
|
||||||
|
@ -143,9 +153,38 @@ public class ConcordServer implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void publishMetaDataToDiscoveryServers() {
|
||||||
|
if (this.config.getDiscoveryServers().isEmpty()) return;
|
||||||
|
ObjectNode node = this.mapper.createObjectNode();
|
||||||
|
node.put("name", this.config.getName());
|
||||||
|
node.put("description", this.config.getDescription());
|
||||||
|
node.put("port", this.config.getPort());
|
||||||
|
String json;
|
||||||
|
try {
|
||||||
|
json = this.mapper.writeValueAsString(node);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
var discoveryServers = List.copyOf(this.config.getDiscoveryServers());
|
||||||
|
for (var discoveryServer : discoveryServers) {
|
||||||
|
System.out.println("Publishing this server's metadata to discovery server at " + discoveryServer);
|
||||||
|
var request = HttpRequest.newBuilder(URI.create(discoveryServer))
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString(json))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.timeout(Duration.ofSeconds(3))
|
||||||
|
.build();
|
||||||
|
try {
|
||||||
|
this.httpClient.send(request, HttpResponse.BodyHandlers.discarding());
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
this.running = true;
|
this.running = true;
|
||||||
|
this.scheduledExecutorService.scheduleAtFixedRate(this::publishMetaDataToDiscoveryServers, 1, 1, TimeUnit.MINUTES);
|
||||||
ServerSocket serverSocket;
|
ServerSocket serverSocket;
|
||||||
try {
|
try {
|
||||||
serverSocket = new ServerSocket(this.config.getPort());
|
serverSocket = new ServerSocket(this.config.getPort());
|
||||||
|
@ -163,6 +202,7 @@ public class ConcordServer implements Runnable {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
this.scheduledExecutorService.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
|
@ -39,5 +39,6 @@ public class RemoveChannelCommand implements ServerCliCommand {
|
||||||
server.getConfig().getChannels().removeIf(channelConfig -> channelConfig.getName().equals(channelToRemove.getName()));
|
server.getConfig().getChannels().removeIf(channelConfig -> channelConfig.getName().equals(channelToRemove.getName()));
|
||||||
server.getConfig().save();
|
server.getConfig().save();
|
||||||
server.broadcast(server.getMetaData());
|
server.broadcast(server.getMetaData());
|
||||||
|
System.out.println("Removed the channel " + channelToRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,15 @@ import java.util.List;
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public final class ServerConfig {
|
public final class ServerConfig {
|
||||||
private String name;
|
private String name;
|
||||||
|
private String description;
|
||||||
private int port;
|
private int port;
|
||||||
private int chatHistoryMaxCount;
|
private int chatHistoryMaxCount;
|
||||||
private int chatHistoryDefaultCount;
|
private int chatHistoryDefaultCount;
|
||||||
private int maxMessageLength;
|
private int maxMessageLength;
|
||||||
private List<ChannelConfig> channels;
|
private List<ChannelConfig> channels;
|
||||||
|
|
||||||
|
private List<String> discoveryServers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The path at which this config is stored.
|
* The path at which this config is stored.
|
||||||
*/
|
*/
|
||||||
|
@ -45,11 +48,13 @@ public final class ServerConfig {
|
||||||
if (Files.notExists(filePath)) {
|
if (Files.notExists(filePath)) {
|
||||||
config = new ServerConfig(
|
config = new ServerConfig(
|
||||||
"My Concord Server",
|
"My Concord Server",
|
||||||
|
"A concord server for my friends and I.",
|
||||||
8123,
|
8123,
|
||||||
100,
|
100,
|
||||||
50,
|
50,
|
||||||
8192,
|
8192,
|
||||||
List.of(new ChannelConfig(idProvider.newId().toString(), "general", "Default channel for general discussion.")),
|
List.of(new ChannelConfig(idProvider.newId().toString(), "general", "Default channel for general discussion.")),
|
||||||
|
List.of(),
|
||||||
filePath
|
filePath
|
||||||
);
|
);
|
||||||
try (var out = Files.newOutputStream(filePath)) {
|
try (var out = Files.newOutputStream(filePath)) {
|
||||||
|
|
Loading…
Reference in New Issue