JMX
This commit is contained in:
parent
8fb0d98f7a
commit
8b1d001ac0
@ -4,14 +4,22 @@ import eu.jonahbauer.wizard.common.messages.server.*;
|
|||||||
import eu.jonahbauer.wizard.common.model.Configuration;
|
import eu.jonahbauer.wizard.common.model.Configuration;
|
||||||
import eu.jonahbauer.wizard.server.debug.DebugSession;
|
import eu.jonahbauer.wizard.server.debug.DebugSession;
|
||||||
import eu.jonahbauer.wizard.server.machine.Player;
|
import eu.jonahbauer.wizard.server.machine.Player;
|
||||||
|
import eu.jonahbauer.wizard.server.management.LobbyMBean;
|
||||||
|
import eu.jonahbauer.wizard.server.management.SessionMBean;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.intellij.lang.annotations.Language;
|
import org.intellij.lang.annotations.Language;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.StandardMBean;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
public class Lobby {
|
@Log4j2
|
||||||
|
public class Lobby implements LobbyMBean {
|
||||||
@Language("RegExp")
|
@Language("RegExp")
|
||||||
private static final String SESSION_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
|
private static final String SESSION_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
|
||||||
private static final Lobby INSTANCE = new Lobby();
|
private static final Lobby INSTANCE = new Lobby();
|
||||||
@ -19,12 +27,17 @@ public class Lobby {
|
|||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final MBeanServer mBeanServer;
|
||||||
|
|
||||||
private final Map<UUID, Session> sessions = new ConcurrentHashMap<>();
|
private final Map<UUID, Session> sessions = new ConcurrentHashMap<>();
|
||||||
private final List<Player> players = new ArrayList<>();
|
private final List<Player> players = new ArrayList<>();
|
||||||
// read lock is required whenever players are read or sessions are modified, write lock is required when players are modified
|
// read lock is required whenever players are read or sessions are modified, write lock is required when players are modified
|
||||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
private Lobby() {}
|
private Lobby() {
|
||||||
|
mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
registerLobbyMBean();
|
||||||
|
}
|
||||||
|
|
||||||
public Session createSession(@NotNull String name, long timeout, @NotNull Configuration configuration) {
|
public Session createSession(@NotNull String name, long timeout, @NotNull Configuration configuration) {
|
||||||
if (!name.matches(SESSION_NAME_PATTERN)) {
|
if (!name.matches(SESSION_NAME_PATTERN)) {
|
||||||
@ -42,6 +55,9 @@ public class Lobby {
|
|||||||
session = new Session(UUID.randomUUID(), name, timeout, configuration);
|
session = new Session(UUID.randomUUID(), name, timeout, configuration);
|
||||||
} while (sessions.putIfAbsent(session.getUuid(), session) != null);
|
} while (sessions.putIfAbsent(session.getUuid(), session) != null);
|
||||||
|
|
||||||
|
log.info("Created session {}.", session.getUuid());
|
||||||
|
registerSessionMBean(session);
|
||||||
|
|
||||||
notifyPlayers(new SessionCreatedMessage(session.toData()));
|
notifyPlayers(new SessionCreatedMessage(session.toData()));
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
@ -58,6 +74,9 @@ public class Lobby {
|
|||||||
session = new DebugSession(UUID.randomUUID(), name, timeout, configuration);
|
session = new DebugSession(UUID.randomUUID(), name, timeout, configuration);
|
||||||
} while (sessions.putIfAbsent(session.getUuid(), session) != null);
|
} while (sessions.putIfAbsent(session.getUuid(), session) != null);
|
||||||
|
|
||||||
|
log.info("Created debug session {}.", session.getUuid());
|
||||||
|
registerSessionMBean(session);
|
||||||
|
|
||||||
notifyPlayers(new SessionCreatedMessage(session.toData()));
|
notifyPlayers(new SessionCreatedMessage(session.toData()));
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
@ -73,8 +92,11 @@ public class Lobby {
|
|||||||
public void removeSession(UUID uuid) {
|
public void removeSession(UUID uuid) {
|
||||||
lock.readLock().lock();
|
lock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
sessions.remove(uuid);
|
if (sessions.remove(uuid) != null) {
|
||||||
notifyPlayers(new SessionRemovedMessage(uuid));
|
log.info("Removed session {}.", uuid);
|
||||||
|
unregisterSessionMBean(uuid);
|
||||||
|
notifyPlayers(new SessionRemovedMessage(uuid));
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
lock.readLock().unlock();
|
lock.readLock().unlock();
|
||||||
}
|
}
|
||||||
@ -117,4 +139,53 @@ public class Lobby {
|
|||||||
lock.readLock().unlock();
|
lock.readLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//<editor-fold desc="JMX" defaultstate="collapsed">
|
||||||
|
private void registerLobbyMBean() {
|
||||||
|
try {
|
||||||
|
var name = new ObjectName("eu.jonahbauer.wizard.server:type=Lobby");
|
||||||
|
mBeanServer.registerMBean(new StandardMBean(this, LobbyMBean.class), name);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Could not register LobbyMBean.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerSessionMBean(@NotNull Session session) {
|
||||||
|
try {
|
||||||
|
var name = new ObjectName("eu.jonahbauer.wizard.server:type=Session,name=" + session.getUuid());
|
||||||
|
mBeanServer.registerMBean(new StandardMBean(session, SessionMBean.class), name);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Could not register SessionMBean for session {}.", session.getUuid(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterSessionMBean(@NotNull UUID uuid) {
|
||||||
|
try {
|
||||||
|
var name = new ObjectName("eu.jonahbauer.wizard.server:type=Session,name=" + uuid);
|
||||||
|
mBeanServer.unregisterMBean(name);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Could not unregister SessionMBean for session {}.", uuid, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPlayerCount() {
|
||||||
|
lock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return players.size();
|
||||||
|
} finally {
|
||||||
|
lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTotalPlayerCount() {
|
||||||
|
return sessions.values().stream().mapToInt(Session::getPlayerCount).sum() + getPlayerCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSessionCount() {
|
||||||
|
return sessions.size();
|
||||||
|
}
|
||||||
|
//</editor-fold>
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package eu.jonahbauer.wizard.server;
|
package eu.jonahbauer.wizard.server;
|
||||||
|
|
||||||
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
|
|
||||||
import eu.jonahbauer.wizard.common.messages.data.PlayerData;
|
import eu.jonahbauer.wizard.common.messages.data.PlayerData;
|
||||||
import eu.jonahbauer.wizard.common.messages.data.SessionData;
|
import eu.jonahbauer.wizard.common.messages.data.SessionData;
|
||||||
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
|
import eu.jonahbauer.wizard.common.messages.observer.ObserverMessage;
|
||||||
@ -14,6 +13,7 @@ import eu.jonahbauer.wizard.core.messages.Observer;
|
|||||||
import eu.jonahbauer.wizard.core.model.Configurations;
|
import eu.jonahbauer.wizard.core.model.Configurations;
|
||||||
import eu.jonahbauer.wizard.core.util.Pair;
|
import eu.jonahbauer.wizard.core.util.Pair;
|
||||||
import eu.jonahbauer.wizard.server.machine.Player;
|
import eu.jonahbauer.wizard.server.machine.Player;
|
||||||
|
import eu.jonahbauer.wizard.server.management.SessionMBean;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@ -29,7 +29,7 @@ import java.util.concurrent.ThreadLocalRandom;
|
|||||||
@Getter
|
@Getter
|
||||||
@Log4j2
|
@Log4j2
|
||||||
@EqualsAndHashCode(of = "uuid")
|
@EqualsAndHashCode(of = "uuid")
|
||||||
public class Session implements Observer {
|
public class Session implements Observer, SessionMBean {
|
||||||
@Language("RegExp")
|
@Language("RegExp")
|
||||||
private static final String PLAYER_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
|
private static final String PLAYER_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
|
||||||
protected static final int MIN_PLAYERS = 3;
|
protected static final int MIN_PLAYERS = 3;
|
||||||
@ -271,6 +271,31 @@ public class Session implements Observer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
for (var sessionPlayer : getPlayers().values()) {
|
||||||
|
var player = sessionPlayer.getPlayer();
|
||||||
|
if (player != null) {
|
||||||
|
player.disconnect("Session was forcibly closed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<editor-fold desc="JMX" defaulstate="collapsed">
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
return game != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDebug() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int getPlayerCount() {
|
||||||
|
return players.size();
|
||||||
|
}
|
||||||
|
//</editor-fold>
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(of = "uuid")
|
@EqualsAndHashCode(of = "uuid")
|
||||||
protected static class SessionPlayer {
|
protected static class SessionPlayer {
|
||||||
|
@ -10,7 +10,6 @@ import eu.jonahbauer.wizard.server.NackException;
|
|||||||
import eu.jonahbauer.wizard.server.Session;
|
import eu.jonahbauer.wizard.server.Session;
|
||||||
import eu.jonahbauer.wizard.server.machine.Player;
|
import eu.jonahbauer.wizard.server.machine.Player;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class DebugSession extends Session {
|
public class DebugSession extends Session {
|
||||||
@ -60,19 +59,13 @@ public class DebugSession extends Session {
|
|||||||
@Override
|
@Override
|
||||||
protected void startGame() {}
|
protected void startGame() {}
|
||||||
|
|
||||||
public void close() {
|
|
||||||
for (var sessionPlayer : getPlayers().values()) {
|
|
||||||
try {
|
|
||||||
var player = sessionPlayer.getPlayer();
|
|
||||||
if (player != null) {
|
|
||||||
player.disconnect();
|
|
||||||
}
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyPlayers(ServerMessage message) {
|
public void notifyPlayers(ServerMessage message) {
|
||||||
super.notifyPlayers(message);
|
super.notifyPlayers(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDebug() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import eu.jonahbauer.wizard.common.messages.server.Response;
|
|||||||
import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
|
import eu.jonahbauer.wizard.common.messages.server.ServerMessage;
|
||||||
import eu.jonahbauer.wizard.server.machine.states.CreatedState;
|
import eu.jonahbauer.wizard.server.machine.states.CreatedState;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.springframework.web.socket.CloseStatus;
|
import org.springframework.web.socket.CloseStatus;
|
||||||
import org.springframework.web.socket.TextMessage;
|
import org.springframework.web.socket.TextMessage;
|
||||||
import org.springframework.web.socket.WebSocketSession;
|
import org.springframework.web.socket.WebSocketSession;
|
||||||
@ -44,8 +45,16 @@ public class Player extends Context<ClientState, Player> {
|
|||||||
shouldBuffer = Response.class;
|
shouldBuffer = Response.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnect() throws IOException {
|
public void disconnect() {
|
||||||
session.close(CloseStatus.SERVER_ERROR);
|
disconnect(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect(@Nullable String reason) {
|
||||||
|
try {
|
||||||
|
session.close(new CloseStatus(CloseStatus.GOING_AWAY.getCode(), reason));
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package eu.jonahbauer.wizard.server.management;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public interface LobbyMBean {
|
||||||
|
|
||||||
|
int getPlayerCount();
|
||||||
|
|
||||||
|
int getTotalPlayerCount();
|
||||||
|
|
||||||
|
int getSessionCount();
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package eu.jonahbauer.wizard.server.management;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public interface SessionMBean {
|
||||||
|
|
||||||
|
UUID getUuid();
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
int getPlayerCount();
|
||||||
|
|
||||||
|
boolean isRunning();
|
||||||
|
|
||||||
|
boolean isDebug();
|
||||||
|
|
||||||
|
void close();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user