Jonah Bauer 3 years ago committed by jbb01
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.server.debug.DebugSession;
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.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.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Lobby {
@Log4j2
public class Lobby implements LobbyMBean {
@Language("RegExp")
private static final String SESSION_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
private static final Lobby INSTANCE = new Lobby();
@ -19,12 +27,17 @@ public class Lobby {
return INSTANCE;
}
private final MBeanServer mBeanServer;
private final Map<UUID, Session> sessions = new ConcurrentHashMap<>();
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
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) {
if (!name.matches(SESSION_NAME_PATTERN)) {
@ -42,6 +55,9 @@ public class Lobby {
session = new Session(UUID.randomUUID(), name, timeout, configuration);
} while (sessions.putIfAbsent(session.getUuid(), session) != null);
log.info("Created session {}.", session.getUuid());
registerSessionMBean(session);
notifyPlayers(new SessionCreatedMessage(session.toData()));
return session;
@ -58,6 +74,9 @@ public class Lobby {
session = new DebugSession(UUID.randomUUID(), name, timeout, configuration);
} while (sessions.putIfAbsent(session.getUuid(), session) != null);
log.info("Created debug session {}.", session.getUuid());
registerSessionMBean(session);
notifyPlayers(new SessionCreatedMessage(session.toData()));
return session;
@ -73,8 +92,11 @@ public class Lobby {
public void removeSession(UUID uuid) {
lock.readLock().lock();
try {
sessions.remove(uuid);
notifyPlayers(new SessionRemovedMessage(uuid));
if (sessions.remove(uuid) != null) {
log.info("Removed session {}.", uuid);
unregisterSessionMBean(uuid);
notifyPlayers(new SessionRemovedMessage(uuid));
}
} finally {
lock.readLock().unlock();
}
@ -117,4 +139,53 @@ public class Lobby {
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;
import eu.jonahbauer.wizard.common.messages.client.InteractionMessage;
import eu.jonahbauer.wizard.common.messages.data.PlayerData;
import eu.jonahbauer.wizard.common.messages.data.SessionData;
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.util.Pair;
import eu.jonahbauer.wizard.server.machine.Player;
import eu.jonahbauer.wizard.server.management.SessionMBean;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -29,7 +29,7 @@ import java.util.concurrent.ThreadLocalRandom;
@Getter
@Log4j2
@EqualsAndHashCode(of = "uuid")
public class Session implements Observer {
public class Session implements Observer, SessionMBean {
@Language("RegExp")
private static final String PLAYER_NAME_PATTERN = "[a-zA-Z0-9_' ]{1,20}";
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
@EqualsAndHashCode(of = "uuid")
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.machine.Player;
import java.io.IOException;
import java.util.UUID;
public class DebugSession extends Session {
@ -60,19 +59,13 @@ public class DebugSession extends Session {
@Override
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
public void notifyPlayers(ServerMessage 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.server.machine.states.CreatedState;
import lombok.SneakyThrows;
import org.jetbrains.annotations.Nullable;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
@ -44,8 +45,16 @@ public class Player extends Context<ClientState, Player> {
shouldBuffer = Response.class;
}
public void disconnect() throws IOException {
session.close(CloseStatus.SERVER_ERROR);
public void disconnect() {
disconnect(null);
}
public void disconnect(@Nullable String reason) {
try {
session.close(new CloseStatus(CloseStatus.GOING_AWAY.getCode(), reason));
} catch (IOException ignored) {
// ignored
}
}
@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…
Cancel
Save