replace singletons
This commit is contained in:
parent
eadf1eaf5b
commit
0477142233
@ -14,7 +14,7 @@ import java.util.List;
|
|||||||
public interface ChatBotSupportMXBean {
|
public interface ChatBotSupportMXBean {
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
static @NotNull ObjectName getObjectName(String name) {
|
static @NotNull ObjectName getObjectName(@NotNull String name) {
|
||||||
return ObjectName.getInstance(STR."eu.jonahbauer.chat.bot.server:component=ChatBots,name=\{quote(name)}");
|
return ObjectName.getInstance(STR."eu.jonahbauer.chat.bot.server:component=ChatBots,name=\{quote(name)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,30 +1,32 @@
|
|||||||
package eu.jonahbauer.chat.server;
|
package eu.jonahbauer.chat.server;
|
||||||
|
|
||||||
import eu.jonahbauer.chat.server.bot.ChatBotSupervisor;
|
import eu.jonahbauer.chat.server.bot.ChatBotSupervisor;
|
||||||
import eu.jonahbauer.chat.server.management.impl.ChatBotManager;
|
|
||||||
import eu.jonahbauer.chat.server.management.impl.SocketManager;
|
|
||||||
import eu.jonahbauer.chat.server.socket.SocketSupervisor;
|
import eu.jonahbauer.chat.server.socket.SocketSupervisor;
|
||||||
|
import eu.jonahbauer.chat.server.util.Lazy.MutableLazy;
|
||||||
|
|
||||||
|
import javax.management.JMException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException, InterruptedException {
|
public static void main(String[] args) throws IOException, InterruptedException, JMException {
|
||||||
// initialize ChatBotManager and SocketManager
|
|
||||||
var _ = ChatBotManager.INSTANCE;
|
|
||||||
var _ = SocketManager.INSTANCE;
|
|
||||||
|
|
||||||
var config = Config.load();
|
var config = Config.load();
|
||||||
ChatBotSupervisor.INSTANCE.start(config);
|
var chatBotSupervisorLazy = new MutableLazy<ChatBotSupervisor>();
|
||||||
SocketSupervisor.INSTANCE.start(config);
|
var socketSupervisorLazy = new MutableLazy<SocketSupervisor>();
|
||||||
|
try (
|
||||||
|
var chatBotSupervisor = chatBotSupervisorLazy.set(new ChatBotSupervisor(socketSupervisorLazy));
|
||||||
|
var socketSupervisor = socketSupervisorLazy.set(new SocketSupervisor(chatBotSupervisorLazy))
|
||||||
|
) {
|
||||||
|
chatBotSupervisor.start(config);
|
||||||
|
socketSupervisor.start(config);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// keep main thread running
|
// keep main thread running
|
||||||
new CountDownLatch(1).await();
|
new CountDownLatch(1).await();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException _) {
|
||||||
SocketSupervisor.INSTANCE.stop();
|
// ignore
|
||||||
ChatBotSupervisor.INSTANCE.stop();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,32 +6,43 @@ import eu.jonahbauer.chat.bot.config.BotConfig;
|
|||||||
import eu.jonahbauer.chat.bot.impl.BotCreationException;
|
import eu.jonahbauer.chat.bot.impl.BotCreationException;
|
||||||
import eu.jonahbauer.chat.bot.impl.ChatBotFactory;
|
import eu.jonahbauer.chat.bot.impl.ChatBotFactory;
|
||||||
import eu.jonahbauer.chat.server.Config;
|
import eu.jonahbauer.chat.server.Config;
|
||||||
|
import eu.jonahbauer.chat.server.management.impl.ChatBotManager;
|
||||||
import eu.jonahbauer.chat.server.management.impl.ChatBotSupport;
|
import eu.jonahbauer.chat.server.management.impl.ChatBotSupport;
|
||||||
import eu.jonahbauer.chat.server.socket.SocketSupervisor;
|
import eu.jonahbauer.chat.server.socket.SocketSupervisor;
|
||||||
|
import eu.jonahbauer.chat.server.util.Lazy;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.jetbrains.annotations.Blocking;
|
import org.jetbrains.annotations.Blocking;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NonBlocking;
|
import org.jetbrains.annotations.NonBlocking;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import javax.management.JMException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
public enum ChatBotSupervisor {
|
public final class ChatBotSupervisor implements AutoCloseable {
|
||||||
INSTANCE;
|
|
||||||
|
|
||||||
private static final Duration POST_EXPIRATION = Duration.ofSeconds(2);
|
private static final Duration POST_EXPIRATION = Duration.ofSeconds(2);
|
||||||
private static final int MESSAGE_QUEUE_SIZE = 10;
|
private static final int MESSAGE_QUEUE_SIZE = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of running bots indexed by their name.
|
* Map of running bots indexed by their name.
|
||||||
*/
|
*/
|
||||||
private final @NotNull Map<@NotNull String, @NotNull RunningBot> bots = new HashMap<>();
|
private final @NotNull ConcurrentMap<@NotNull String, @NotNull RunningBot> bots = new ConcurrentHashMap<>();
|
||||||
|
private final @NotNull ReentrantLock lock = new ReentrantLock();
|
||||||
|
private final @NotNull Lazy<SocketSupervisor> socketSupervisor;
|
||||||
|
|
||||||
|
public ChatBotSupervisor(@NotNull Lazy<SocketSupervisor> socketSupervisor) throws JMException {
|
||||||
|
ChatBotManager.init(this);
|
||||||
|
this.socketSupervisor = socketSupervisor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatches the message to all running bots. If a bot has too many messages queued up, the message may not be
|
* Dispatches the message to all running bots. If a bot has too many messages queued up, the message may not be
|
||||||
@ -52,9 +63,16 @@ public enum ChatBotSupervisor {
|
|||||||
* @param config a configuration
|
* @param config a configuration
|
||||||
* @throws IllegalStateException if any bots are running already
|
* @throws IllegalStateException if any bots are running already
|
||||||
*/
|
*/
|
||||||
public synchronized void start(@NotNull Config config) {
|
public void start(@NotNull Config config) {
|
||||||
if (!bots.isEmpty()) throw new IllegalStateException("start(Config) may not be used when any bots are running already");
|
lock.lock();
|
||||||
config.bots().forEach(this::start);
|
try {
|
||||||
|
if (!bots.isEmpty()) {
|
||||||
|
throw new IllegalStateException("start(Config) may not be used when any bots are running already");
|
||||||
|
}
|
||||||
|
config.bots().forEach(this::start);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,7 +82,7 @@ public enum ChatBotSupervisor {
|
|||||||
* @param channels the channels the bot will be active in
|
* @param channels the channels the bot will be active in
|
||||||
* @throws BotCreationException if the bot could not be created
|
* @throws BotCreationException if the bot could not be created
|
||||||
*/
|
*/
|
||||||
public synchronized void start(@NotNull String name, @NotNull String type, @NotNull List<String> channels) {
|
public void start(@NotNull String name, @NotNull String type, @NotNull List<String> channels) {
|
||||||
start(name, BotConfig.builder().type(type).channels(channels).build());
|
start(name, BotConfig.builder().type(type).channels(channels).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,9 +92,16 @@ public enum ChatBotSupervisor {
|
|||||||
* @param config the bot configuration
|
* @param config the bot configuration
|
||||||
* @throws BotCreationException if the bot could not be created
|
* @throws BotCreationException if the bot could not be created
|
||||||
*/
|
*/
|
||||||
public synchronized void start(@NotNull String name, @NotNull BotConfig config) {
|
public void start(@NotNull String name, @NotNull BotConfig config) {
|
||||||
if (bots.containsKey(name)) throw new BotCreationException("Duplicate bot name: " + name);
|
lock.lock();
|
||||||
bots.put(name, new RunningBot(name, config));
|
try {
|
||||||
|
if (bots.containsKey(name)) {
|
||||||
|
throw new BotCreationException("Duplicate bot name: " + name);
|
||||||
|
}
|
||||||
|
bots.put(name, new RunningBot(name, config));
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,10 +112,13 @@ public enum ChatBotSupervisor {
|
|||||||
public void stop(@NotNull String name) throws InterruptedException {
|
public void stop(@NotNull String name) throws InterruptedException {
|
||||||
RunningBot bot;
|
RunningBot bot;
|
||||||
|
|
||||||
synchronized (this) {
|
lock.lock();
|
||||||
|
try {
|
||||||
bot = bots.remove(name);
|
bot = bots.remove(name);
|
||||||
if (bot == null) return;
|
if (bot == null) return;
|
||||||
bot.stop();
|
bot.stop();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
bot.join();
|
bot.join();
|
||||||
@ -103,12 +131,15 @@ public enum ChatBotSupervisor {
|
|||||||
public void stop() throws InterruptedException {
|
public void stop() throws InterruptedException {
|
||||||
var stopped = new ArrayList<RunningBot>();
|
var stopped = new ArrayList<RunningBot>();
|
||||||
|
|
||||||
synchronized (this) {
|
lock.lock();
|
||||||
|
try {
|
||||||
for (RunningBot bot : bots.values()) {
|
for (RunningBot bot : bots.values()) {
|
||||||
stopped.add(bot);
|
stopped.add(bot);
|
||||||
bot.stop();
|
bot.stop();
|
||||||
}
|
}
|
||||||
bots.clear();
|
bots.clear();
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var bot : stopped) {
|
for (var bot : stopped) {
|
||||||
@ -116,6 +147,11 @@ public enum ChatBotSupervisor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws InterruptedException {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@return a map of all currently running bots and their effective config}
|
* {@return a map of all currently running bots and their effective config}
|
||||||
*/
|
*/
|
||||||
@ -144,10 +180,10 @@ public enum ChatBotSupervisor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages a {@link ChatBot} instance running on its own thread. Takes care of recreating the bot after it threw
|
* Manages a {@link ChatBot} instance running on its own thread. Takes care of recreating the bot after it threw
|
||||||
* an exception. {@linkplain ChatBotSupport#register(String, BotConfig) Registers} the bot during construction
|
* an exception. {@linkplain ChatBotSupport#register(ChatBotSupervisor, String, BotConfig) Registers} the bot
|
||||||
* and {@linkplain ChatBotSupport#unregister(String) unregisters} it during {@link #stop()}.
|
* during construction and {@linkplain ChatBotSupport#unregister(String) unregisters} it during {@link #stop()}.
|
||||||
*/
|
*/
|
||||||
private static class RunningBot implements Runnable {
|
private class RunningBot implements Runnable {
|
||||||
private final @NotNull String name;
|
private final @NotNull String name;
|
||||||
private final @NotNull BotConfig config;
|
private final @NotNull BotConfig config;
|
||||||
private final @NotNull ChatBotFactory<?> factory;
|
private final @NotNull ChatBotFactory<?> factory;
|
||||||
@ -163,7 +199,7 @@ public enum ChatBotSupervisor {
|
|||||||
this.factory = getChatBotFactory(config.getType());
|
this.factory = getChatBotFactory(config.getType());
|
||||||
|
|
||||||
log.info("starting bot {}...", name);
|
log.info("starting bot {}...", name);
|
||||||
ChatBotSupport.register(name, config);
|
ChatBotSupport.register(ChatBotSupervisor.this, name, config);
|
||||||
this.thread = Thread.ofVirtual().name("ChatBot[" + name + "]").start(this);
|
this.thread = Thread.ofVirtual().name("ChatBot[" + name + "]").start(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,7 +236,7 @@ public enum ChatBotSupervisor {
|
|||||||
} else if (!bot.getConfig().getChannels().contains(post.post().channel())) {
|
} else if (!bot.getConfig().getChannels().contains(post.post().channel())) {
|
||||||
log.debug("Ignoring message {} because channel is not listened to.", post.post());
|
log.debug("Ignoring message {} because channel is not listened to.", post.post());
|
||||||
} else {
|
} else {
|
||||||
bot.onMessage(SocketSupervisor.INSTANCE, post.post());
|
bot.onMessage(ChatBotSupervisor.this.socketSupervisor.get(), post.post());
|
||||||
}
|
}
|
||||||
} catch (InterruptedException _) {
|
} catch (InterruptedException _) {
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import eu.jonahbauer.chat.server.management.annotations.ManagedBean;
|
|||||||
import eu.jonahbauer.chat.server.management.annotations.ManagedOperation;
|
import eu.jonahbauer.chat.server.management.annotations.ManagedOperation;
|
||||||
import eu.jonahbauer.chat.server.management.annotations.ManagedParameter;
|
import eu.jonahbauer.chat.server.management.annotations.ManagedParameter;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.management.*;
|
import javax.management.*;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -15,13 +17,13 @@ import java.util.Locale;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
public class AdvancedMBean extends StandardMBean {
|
class AdvancedMBean extends StandardMBean {
|
||||||
public <T> AdvancedMBean(T implementation) {
|
public AdvancedMBean(@NotNull Object implementation) {
|
||||||
super(implementation, null, true);
|
super(implementation, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDescription(MBeanInfo info) {
|
protected @Nullable String getDescription(@Nullable MBeanInfo info) {
|
||||||
var clazz = getMBeanInterface();
|
var clazz = getMBeanInterface();
|
||||||
var annotation = clazz.getAnnotation(ManagedBean.class);
|
var annotation = clazz.getAnnotation(ManagedBean.class);
|
||||||
if (annotation != null && !"".equals(annotation.description())) {
|
if (annotation != null && !"".equals(annotation.description())) {
|
||||||
@ -32,9 +34,7 @@ public class AdvancedMBean extends StandardMBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getImpact(MBeanOperationInfo info) {
|
protected int getImpact(@Nullable MBeanOperationInfo info) {
|
||||||
if (info == null) return MBeanOperationInfo.UNKNOWN;
|
|
||||||
|
|
||||||
var method = findOperation(info);
|
var method = findOperation(info);
|
||||||
var annotation = method != null ? method.getAnnotation(ManagedOperation.class) : null;
|
var annotation = method != null ? method.getAnnotation(ManagedOperation.class) : null;
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
@ -45,9 +45,7 @@ public class AdvancedMBean extends StandardMBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDescription(MBeanOperationInfo info) {
|
protected @Nullable String getDescription(@Nullable MBeanOperationInfo info) {
|
||||||
if (info == null) return null;
|
|
||||||
|
|
||||||
var method = findOperation(info);
|
var method = findOperation(info);
|
||||||
var annotation = method != null ? method.getAnnotation(ManagedOperation.class) : null;
|
var annotation = method != null ? method.getAnnotation(ManagedOperation.class) : null;
|
||||||
if (annotation != null && !"".equals(annotation.description())) {
|
if (annotation != null && !"".equals(annotation.description())) {
|
||||||
@ -58,13 +56,9 @@ public class AdvancedMBean extends StandardMBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getParameterName(MBeanOperationInfo op, MBeanParameterInfo param, int sequence) {
|
protected @Nullable String getParameterName(@Nullable MBeanOperationInfo op, @Nullable MBeanParameterInfo param, int sequence) {
|
||||||
if (op == null) return super.getParameterName(op, param, sequence);
|
|
||||||
|
|
||||||
var method = findOperation(op);
|
var method = findOperation(op);
|
||||||
if (method == null) return super.getParameterName(op, param, sequence);
|
var annotation = method != null ? method.getParameters()[sequence].getAnnotation(ManagedParameter.class) : null;
|
||||||
|
|
||||||
var annotation = method.getParameters()[sequence].getAnnotation(ManagedParameter.class);
|
|
||||||
if (annotation != null && !"".equals(annotation.name())) {
|
if (annotation != null && !"".equals(annotation.name())) {
|
||||||
return annotation.name();
|
return annotation.name();
|
||||||
} else {
|
} else {
|
||||||
@ -73,13 +67,9 @@ public class AdvancedMBean extends StandardMBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDescription(MBeanOperationInfo op, MBeanParameterInfo param, int sequence) {
|
protected @Nullable String getDescription(@Nullable MBeanOperationInfo op, @Nullable MBeanParameterInfo param, int sequence) {
|
||||||
if (op == null) return super.getParameterName(op, param, sequence);
|
|
||||||
|
|
||||||
var method = findOperation(op);
|
var method = findOperation(op);
|
||||||
if (method == null) return super.getParameterName(op, param, sequence);
|
var annotation = method != null ? method.getParameters()[sequence].getAnnotation(ManagedParameter.class) : null;
|
||||||
|
|
||||||
var annotation = method.getParameters()[sequence].getAnnotation(ManagedParameter.class);
|
|
||||||
if (annotation != null && !"".equals(annotation.description())) {
|
if (annotation != null && !"".equals(annotation.description())) {
|
||||||
return annotation.description();
|
return annotation.description();
|
||||||
} else {
|
} else {
|
||||||
@ -88,7 +78,9 @@ public class AdvancedMBean extends StandardMBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getDescription(MBeanAttributeInfo info) {
|
protected @Nullable String getDescription(@Nullable MBeanAttributeInfo info) {
|
||||||
|
if (info == null) return super.getDescription(info);
|
||||||
|
|
||||||
var name = info.getName();
|
var name = info.getName();
|
||||||
var type = getType(info, MBeanAttributeInfo::getType);
|
var type = getType(info, MBeanAttributeInfo::getType);
|
||||||
|
|
||||||
@ -107,7 +99,7 @@ public class AdvancedMBean extends StandardMBean {
|
|||||||
return super.getDescription(info);
|
return super.getDescription(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends MBeanFeatureInfo> String getType(T info, Function<T, String> getType) {
|
private <T extends MBeanFeatureInfo> String getType(@NotNull T info, @NotNull Function<T, String> getType) {
|
||||||
var descriptor = info.getDescriptor();
|
var descriptor = info.getDescriptor();
|
||||||
if (descriptor == null) return getType.apply(info);
|
if (descriptor == null) return getType.apply(info);
|
||||||
|
|
||||||
@ -119,7 +111,9 @@ public class AdvancedMBean extends StandardMBean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method findOperation(MBeanOperationInfo info) {
|
private @Nullable Method findOperation(@Nullable MBeanOperationInfo info) {
|
||||||
|
if (info == null) return null;
|
||||||
|
|
||||||
var name = info.getName();
|
var name = info.getName();
|
||||||
var params = new ArrayList<String>();
|
var params = new ArrayList<String>();
|
||||||
|
|
||||||
@ -130,11 +124,11 @@ public class AdvancedMBean extends StandardMBean {
|
|||||||
return findMethod(name, params);
|
return findMethod(name, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method findMethod(String name, String...parameters) {
|
private @Nullable Method findMethod(@NotNull String name, @NotNull String @NotNull... parameters) {
|
||||||
return findMethod(name, Arrays.asList(parameters));
|
return findMethod(name, Arrays.asList(parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method findMethod(String name, List<String> parameters) {
|
private @Nullable Method findMethod(@NotNull String name, @NotNull List<@NotNull String> parameters) {
|
||||||
var clazz = getMBeanInterface();
|
var clazz = getMBeanInterface();
|
||||||
methods: for (var method : clazz.getMethods()) {
|
methods: for (var method : clazz.getMethods()) {
|
||||||
if (!method.getName().equals(name)) continue;
|
if (!method.getName().equals(name)) continue;
|
||||||
@ -150,7 +144,7 @@ public class AdvancedMBean extends StandardMBean {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String capitalize(String name) {
|
private static @NotNull String capitalize(@NotNull String name) {
|
||||||
if (name.isEmpty()) return name;
|
if (name.isEmpty()) return name;
|
||||||
return name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1);
|
return name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1);
|
||||||
}
|
}
|
||||||
|
@ -5,26 +5,31 @@ import eu.jonahbauer.chat.bot.impl.ChatBotFactory;
|
|||||||
import eu.jonahbauer.chat.server.bot.ChatBotSupervisor;
|
import eu.jonahbauer.chat.server.bot.ChatBotSupervisor;
|
||||||
import eu.jonahbauer.chat.server.management.BotConfigSupport;
|
import eu.jonahbauer.chat.server.management.BotConfigSupport;
|
||||||
import eu.jonahbauer.chat.server.management.ChatBotManagerMXBean;
|
import eu.jonahbauer.chat.server.management.ChatBotManagerMXBean;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
|
import javax.management.JMException;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public enum ChatBotManager implements ChatBotManagerMXBean {
|
public final class ChatBotManager implements ChatBotManagerMXBean {
|
||||||
INSTANCE;
|
private final @NotNull ChatBotSupervisor supervisor;
|
||||||
|
|
||||||
@SneakyThrows
|
public static void init(@NotNull ChatBotSupervisor supervisor) throws JMException {
|
||||||
ChatBotManager() {
|
var impl = new ChatBotManager(supervisor);
|
||||||
var server = ManagementFactory.getPlatformMBeanServer();
|
var server = ManagementFactory.getPlatformMBeanServer();
|
||||||
server.registerMBean(new AdvancedMBean(this), ChatBotManagerMXBean.NAME);
|
server.registerMBean(new AdvancedMBean(impl), ChatBotManagerMXBean.NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChatBotManager(@NotNull ChatBotSupervisor supervisor) {
|
||||||
|
this.supervisor = Objects.requireNonNull(supervisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(@NotNull String name, @NotNull String type, @NotNull List<@NotNull String> channels) {
|
public void start(@NotNull String name, @NotNull String type, @NotNull List<@NotNull String> channels) {
|
||||||
try {
|
try {
|
||||||
ChatBotSupervisor.INSTANCE.start(name, type, channels);
|
supervisor.start(name, type, channels);
|
||||||
} catch (BotCreationException ex) {
|
} catch (BotCreationException ex) {
|
||||||
throw new IllegalArgumentException(ex.getMessage(), ex.getCause());
|
throw new IllegalArgumentException(ex.getMessage(), ex.getCause());
|
||||||
}
|
}
|
||||||
@ -33,7 +38,7 @@ public enum ChatBotManager implements ChatBotManagerMXBean {
|
|||||||
@Override
|
@Override
|
||||||
public void start(@NotNull String name, @NotNull BotConfigSupport config) {
|
public void start(@NotNull String name, @NotNull BotConfigSupport config) {
|
||||||
try {
|
try {
|
||||||
ChatBotSupervisor.INSTANCE.start(name, config.unwrap());
|
supervisor.start(name, config.unwrap());
|
||||||
} catch (BotCreationException ex) {
|
} catch (BotCreationException ex) {
|
||||||
throw new IllegalArgumentException(ex.getMessage(), ex.getCause());
|
throw new IllegalArgumentException(ex.getMessage(), ex.getCause());
|
||||||
}
|
}
|
||||||
@ -41,23 +46,23 @@ public enum ChatBotManager implements ChatBotManagerMXBean {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop(@NotNull String name) throws InterruptedException {
|
public void stop(@NotNull String name) throws InterruptedException {
|
||||||
ChatBotSupervisor.INSTANCE.stop(name);
|
supervisor.stop(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws InterruptedException {
|
public void stop() throws InterruptedException {
|
||||||
ChatBotSupervisor.INSTANCE.stop();
|
supervisor.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Map<@NotNull String, @NotNull BotConfigSupport> getBots() {
|
public @Unmodifiable @NotNull Map<@NotNull String, @NotNull BotConfigSupport> getBots() {
|
||||||
var out = new TreeMap<String, BotConfigSupport>();
|
var out = new TreeMap<String, BotConfigSupport>();
|
||||||
ChatBotSupervisor.INSTANCE.getBots().forEach((key, value) -> out.put(key, new BotConfigSupport(value)));
|
supervisor.getBots().forEach((key, value) -> out.put(key, new BotConfigSupport(value)));
|
||||||
return Collections.unmodifiableMap(out);
|
return Collections.unmodifiableMap(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull SortedSet<@NotNull String> getBotImplementations() {
|
public @Unmodifiable @NotNull SortedSet<@NotNull String> getBotImplementations() {
|
||||||
return Collections.unmodifiableSortedSet(
|
return Collections.unmodifiableSortedSet(
|
||||||
ChatBotFactory.implementations().stream()
|
ChatBotFactory.implementations().stream()
|
||||||
.map(Class::getCanonicalName)
|
.map(Class::getCanonicalName)
|
||||||
|
@ -6,25 +6,29 @@ import eu.jonahbauer.chat.server.management.BotConfigSupport;
|
|||||||
import eu.jonahbauer.chat.server.management.ChatBotSupportMXBean;
|
import eu.jonahbauer.chat.server.management.ChatBotSupportMXBean;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Log4j2
|
@Log4j2
|
||||||
public class ChatBotSupport implements ChatBotSupportMXBean {
|
public final class ChatBotSupport implements ChatBotSupportMXBean {
|
||||||
private final String name;
|
private final @NotNull ChatBotSupervisor supervisor;
|
||||||
private final BotConfigSupport config;
|
private final @NotNull String name;
|
||||||
|
private final @NotNull BotConfigSupport config;
|
||||||
|
|
||||||
public static void register(String name, BotConfig config) {
|
public static void register(@NotNull ChatBotSupervisor supervisor, @NotNull String name, @NotNull BotConfig config) {
|
||||||
try {
|
try {
|
||||||
|
var impl = new ChatBotSupport(supervisor, name, config);
|
||||||
var server = ManagementFactory.getPlatformMBeanServer();
|
var server = ManagementFactory.getPlatformMBeanServer();
|
||||||
server.registerMBean(new AdvancedMBean(new ChatBotSupport(name, config)), ChatBotSupportMXBean.getObjectName(name));
|
server.registerMBean(new AdvancedMBean(impl), ChatBotSupportMXBean.getObjectName(name));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("Could not register bot as an MBean.", ex);
|
log.error("Could not register bot as an MBean.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void unregister(String name) {
|
public static void unregister(@NotNull String name) {
|
||||||
try {
|
try {
|
||||||
var server = ManagementFactory.getPlatformMBeanServer();
|
var server = ManagementFactory.getPlatformMBeanServer();
|
||||||
server.unregisterMBean(ChatBotSupportMXBean.getObjectName(name));
|
server.unregisterMBean(ChatBotSupportMXBean.getObjectName(name));
|
||||||
@ -33,12 +37,13 @@ public class ChatBotSupport implements ChatBotSupportMXBean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChatBotSupport(String name, BotConfig config) {
|
private ChatBotSupport(@NotNull ChatBotSupervisor supervisor, @NotNull String name, @NotNull BotConfig config) {
|
||||||
this.name = name;
|
this.supervisor = Objects.requireNonNull(supervisor);
|
||||||
|
this.name = Objects.requireNonNull(name);
|
||||||
this.config = new BotConfigSupport(config);
|
this.config = new BotConfigSupport(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() throws InterruptedException {
|
public void stop() throws InterruptedException {
|
||||||
ChatBotSupervisor.INSTANCE.stop(name);
|
supervisor.stop(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,43 +2,48 @@ package eu.jonahbauer.chat.server.management.impl;
|
|||||||
|
|
||||||
import eu.jonahbauer.chat.server.management.SocketManagerMXBean;
|
import eu.jonahbauer.chat.server.management.SocketManagerMXBean;
|
||||||
import eu.jonahbauer.chat.server.socket.SocketSupervisor;
|
import eu.jonahbauer.chat.server.socket.SocketSupervisor;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import javax.management.JMException;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
|
||||||
public enum SocketManager implements SocketManagerMXBean {
|
public final class SocketManager implements SocketManagerMXBean {
|
||||||
INSTANCE;
|
private final @NotNull SocketSupervisor supervisor;
|
||||||
|
|
||||||
@SneakyThrows
|
public static void init(@NotNull SocketSupervisor supervisor) throws JMException {
|
||||||
SocketManager() {
|
var impl = new SocketManager(supervisor);
|
||||||
var server = ManagementFactory.getPlatformMBeanServer();
|
var server = ManagementFactory.getPlatformMBeanServer();
|
||||||
server.registerMBean(new AdvancedMBean(this), SocketManagerMXBean.NAME);
|
server.registerMBean(new AdvancedMBean(impl), SocketManagerMXBean.NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SocketManager(@NotNull SocketSupervisor supervisor) {
|
||||||
|
this.supervisor = Objects.requireNonNull(supervisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCredentials(@NotNull String username, @NotNull String password) {
|
public void setCredentials(@NotNull String username, @NotNull String password) {
|
||||||
SocketSupervisor.INSTANCE.setAccount(username, password);
|
supervisor.setAccount(username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(@NotNull String channel) {
|
public void start(@NotNull String channel) {
|
||||||
SocketSupervisor.INSTANCE.start(channel);
|
supervisor.start(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop(@NotNull String channel) throws InterruptedException {
|
public void stop(@NotNull String channel) throws InterruptedException {
|
||||||
SocketSupervisor.INSTANCE.stop(channel);
|
supervisor.stop(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws InterruptedException {
|
public void stop() throws InterruptedException {
|
||||||
SocketSupervisor.INSTANCE.stop();
|
supervisor.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull SortedSet<@NotNull String> getChannels() {
|
public @NotNull SortedSet<@NotNull String> getChannels() {
|
||||||
return SocketSupervisor.INSTANCE.getChannels();
|
return supervisor.getChannels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,30 +3,32 @@ package eu.jonahbauer.chat.server.management.impl;
|
|||||||
import eu.jonahbauer.chat.server.management.SocketState;
|
import eu.jonahbauer.chat.server.management.SocketState;
|
||||||
import eu.jonahbauer.chat.server.management.SocketSupportMXBean;
|
import eu.jonahbauer.chat.server.management.SocketSupportMXBean;
|
||||||
import eu.jonahbauer.chat.server.socket.SocketSupervisor;
|
import eu.jonahbauer.chat.server.socket.SocketSupervisor;
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
@Getter
|
@Getter
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
public final class SocketSupport implements SocketSupportMXBean {
|
||||||
public class SocketSupport implements SocketSupportMXBean {
|
private final @NotNull SocketSupervisor supervisor;
|
||||||
private final String channel;
|
private final @NotNull String channel;
|
||||||
|
|
||||||
public static void register(String channel) {
|
public static void register(@NotNull SocketSupervisor supervisor, @NotNull String channel) {
|
||||||
try {
|
try {
|
||||||
|
var impl = new SocketSupport(supervisor, channel);
|
||||||
var server = ManagementFactory.getPlatformMBeanServer();
|
var server = ManagementFactory.getPlatformMBeanServer();
|
||||||
server.registerMBean(new AdvancedMBean(new SocketSupport(channel)), SocketSupportMXBean.getObjectName(channel));
|
server.registerMBean(new AdvancedMBean(impl), SocketSupportMXBean.getObjectName(channel));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("Could not register socket as an MBean.", ex);
|
log.error("Could not register socket as an MBean.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void unregister(String name) {
|
public static void unregister(@NotNull String name) {
|
||||||
try {
|
try {
|
||||||
var server = ManagementFactory.getPlatformMBeanServer();
|
var server = ManagementFactory.getPlatformMBeanServer();
|
||||||
server.unregisterMBean(SocketSupportMXBean.getObjectName(name));
|
server.unregisterMBean(SocketSupportMXBean.getObjectName(name));
|
||||||
@ -35,30 +37,35 @@ public class SocketSupport implements SocketSupportMXBean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SocketSupport(@NotNull SocketSupervisor supervisor, @NotNull String channel) {
|
||||||
|
this.supervisor = Objects.requireNonNull(supervisor);
|
||||||
|
this.channel = Objects.requireNonNull(channel);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Date getCooldownUntil() {
|
public @Nullable Date getCooldownUntil() {
|
||||||
var cooldown = SocketSupervisor.INSTANCE.getCooldownUntil(channel);
|
var cooldown = supervisor.getCooldownUntil(channel);
|
||||||
return cooldown != null ? Date.from(cooldown) : null;
|
return cooldown != null ? Date.from(cooldown) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SocketState getState() {
|
public @Nullable SocketState getState() {
|
||||||
return SocketSupervisor.INSTANCE.getState(channel);
|
return supervisor.getState(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws InterruptedException {
|
public void stop() throws InterruptedException {
|
||||||
SocketSupervisor.INSTANCE.stop(channel);
|
supervisor.stop(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restart() {
|
public void restart() {
|
||||||
SocketSupervisor.INSTANCE.restart(channel);
|
supervisor.restart(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(String name, String message, boolean bottag, boolean publicid) {
|
public void send(@NotNull String name, @NotNull String message, boolean bottag, boolean publicid) {
|
||||||
SocketSupervisor.INSTANCE.send(channel, name, message, bottag, publicid);
|
supervisor.send(channel, name, message, bottag, publicid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,16 @@ import eu.jonahbauer.chat.bot.api.Message;
|
|||||||
import eu.jonahbauer.chat.server.Config;
|
import eu.jonahbauer.chat.server.Config;
|
||||||
import eu.jonahbauer.chat.server.bot.ChatBotSupervisor;
|
import eu.jonahbauer.chat.server.bot.ChatBotSupervisor;
|
||||||
import eu.jonahbauer.chat.server.management.SocketState;
|
import eu.jonahbauer.chat.server.management.SocketState;
|
||||||
|
import eu.jonahbauer.chat.server.management.impl.SocketManager;
|
||||||
import eu.jonahbauer.chat.server.management.impl.SocketSupport;
|
import eu.jonahbauer.chat.server.management.impl.SocketSupport;
|
||||||
|
import eu.jonahbauer.chat.server.util.Lazy;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.apache.logging.log4j.Level;
|
import org.apache.logging.log4j.Level;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.management.JMException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@ -32,9 +35,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
import static eu.jonahbauer.chat.server.util.UrlTemplateProcessor.URL;
|
import static eu.jonahbauer.chat.server.util.UrlTemplateProcessor.URL;
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
public enum SocketSupervisor implements Chat {
|
public final class SocketSupervisor implements Chat, AutoCloseable {
|
||||||
INSTANCE;
|
|
||||||
|
|
||||||
private static final URI AUTH_SERVER = URI.create("https://chat.qed-verein.de/rubychat/account");
|
private static final URI AUTH_SERVER = URI.create("https://chat.qed-verein.de/rubychat/account");
|
||||||
private static final String ORIGIN = "https://chat.qed-verein.de";
|
private static final String ORIGIN = "https://chat.qed-verein.de";
|
||||||
private static final String SERVER = "wss://chat.qed-verein.de/websocket?position=0&version=2&channel=";
|
private static final String SERVER = "wss://chat.qed-verein.de/websocket?position=0&version=2&channel=";
|
||||||
@ -49,6 +50,13 @@ public enum SocketSupervisor implements Chat {
|
|||||||
private final @NotNull CookieManager cookie = new CookieManager();
|
private final @NotNull CookieManager cookie = new CookieManager();
|
||||||
private final @NotNull HttpClient client = HttpClient.newBuilder().cookieHandler(cookie).build();
|
private final @NotNull HttpClient client = HttpClient.newBuilder().cookieHandler(cookie).build();
|
||||||
private final @NotNull ConcurrentMap<@NotNull String, @NotNull ChatClient> sockets = new ConcurrentHashMap<>();
|
private final @NotNull ConcurrentMap<@NotNull String, @NotNull ChatClient> sockets = new ConcurrentHashMap<>();
|
||||||
|
private final @NotNull Lazy<ChatBotSupervisor> chatBotSupervisor;
|
||||||
|
private final @NotNull ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
public SocketSupervisor(@NotNull Lazy<ChatBotSupervisor> chatBotSupervisor) throws JMException {
|
||||||
|
SocketManager.init(this);
|
||||||
|
this.chatBotSupervisor = chatBotSupervisor;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAccount(@NotNull Config.Account account) {
|
public void setAccount(@NotNull Config.Account account) {
|
||||||
this.account = Objects.requireNonNull(account, "account");
|
this.account = Objects.requireNonNull(account, "account");
|
||||||
@ -82,11 +90,18 @@ public enum SocketSupervisor implements Chat {
|
|||||||
* @param config a configuration
|
* @param config a configuration
|
||||||
* @throws IllegalStateException if any sockets are already running
|
* @throws IllegalStateException if any sockets are already running
|
||||||
*/
|
*/
|
||||||
public synchronized void start(@NotNull Config config) {
|
public void start(@NotNull Config config) {
|
||||||
if (!this.sockets.isEmpty()) throw new IllegalStateException("start(Config) may not be used when any sockets are running already");
|
lock.lock();
|
||||||
|
try {
|
||||||
|
if (!this.sockets.isEmpty()) {
|
||||||
|
throw new IllegalStateException("start(Config) may not be used when any sockets are running already");
|
||||||
|
}
|
||||||
|
|
||||||
setAccount(config.account());
|
setAccount(config.account());
|
||||||
config.channels().forEach(this::start);
|
config.channels().forEach(this::start);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,11 +109,18 @@ public enum SocketSupervisor implements Chat {
|
|||||||
* @param channel the channel
|
* @param channel the channel
|
||||||
* @throws IllegalStateException if a socket is already connected to that channel
|
* @throws IllegalStateException if a socket is already connected to that channel
|
||||||
*/
|
*/
|
||||||
public synchronized void start(@NotNull String channel) {
|
public void start(@NotNull String channel) {
|
||||||
if (sockets.containsKey(channel)) throw new SocketCreationException("Duplicate channel: " + channel);
|
lock.lock();
|
||||||
|
try {
|
||||||
|
if (sockets.containsKey(channel)) {
|
||||||
|
throw new SocketCreationException("Duplicate channel: " + channel);
|
||||||
|
}
|
||||||
|
|
||||||
var socket = new ChatClient(channel);
|
var socket = new ChatClient(channel);
|
||||||
this.sockets.put(channel, socket);
|
this.sockets.put(channel, socket);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,6 +173,11 @@ public enum SocketSupervisor implements Chat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws InterruptedException {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
public @NotNull SortedSet<@NotNull String> getChannels() {
|
public @NotNull SortedSet<@NotNull String> getChannels() {
|
||||||
return Collections.unmodifiableSortedSet(new TreeSet<>(sockets.keySet()));
|
return Collections.unmodifiableSortedSet(new TreeSet<>(sockets.keySet()));
|
||||||
}
|
}
|
||||||
@ -191,7 +218,7 @@ public enum SocketSupervisor implements Chat {
|
|||||||
return credentials;
|
return credentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
private record Credentials(String userid, String pwhash) {}
|
private record Credentials(@NotNull String userid, @NotNull String pwhash) {}
|
||||||
|
|
||||||
|
|
||||||
private sealed interface ChatClientState {
|
private sealed interface ChatClientState {
|
||||||
@ -218,7 +245,7 @@ public enum SocketSupervisor implements Chat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class ChatClient implements WebSocket.Listener, ChatClientState {
|
private final class ChatClient implements WebSocket.Listener, ChatClientState {
|
||||||
private final String channel;
|
private final @NotNull String channel;
|
||||||
private final @NotNull ReentrantLock lock = new ReentrantLock();
|
private final @NotNull ReentrantLock lock = new ReentrantLock();
|
||||||
private final @NotNull CountDownLatch stopped = new CountDownLatch(1);
|
private final @NotNull CountDownLatch stopped = new CountDownLatch(1);
|
||||||
|
|
||||||
@ -227,7 +254,7 @@ public enum SocketSupervisor implements Chat {
|
|||||||
|
|
||||||
public ChatClient(@NotNull String channel) {
|
public ChatClient(@NotNull String channel) {
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
SocketSupport.register(channel);
|
SocketSupport.register(SocketSupervisor.this, channel);
|
||||||
this.state.onEnter();
|
this.state.onEnter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,7 +454,7 @@ public enum SocketSupervisor implements Chat {
|
|||||||
|
|
||||||
if (message instanceof Message.Post post) {
|
if (message instanceof Message.Post post) {
|
||||||
delay = post.id() + 1;
|
delay = post.id() + 1;
|
||||||
ChatBotSupervisor.INSTANCE.onMessage(post);
|
SocketSupervisor.this.chatBotSupervisor.get().onMessage(post);
|
||||||
}
|
}
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
log.warn("Could not parse as message: {}", text, e);
|
log.warn("Could not parse as message: {}", text, e);
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package eu.jonahbauer.chat.server.util;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public interface Lazy<T> {
|
||||||
|
@NotNull T get();
|
||||||
|
|
||||||
|
class MutableLazy<T> implements Lazy<T> {
|
||||||
|
private T value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull T get() {
|
||||||
|
var value = this.value;
|
||||||
|
if (value == null) throw new IllegalStateException();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull T set(@NotNull T value) {
|
||||||
|
if (this.value != null) throw new IllegalStateException();
|
||||||
|
this.value = Objects.requireNonNull(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user