Archived
0

MCSM: рефакторинг класса Manager

This commit is contained in:
2017-06-11 22:52:07 +03:00
parent f23e4e8532
commit 88076d2c25
10 changed files with 93 additions and 111 deletions

View File

@@ -29,9 +29,7 @@ public class Activator implements BundleActivator, ServiceListener {
Config config = serviceConfigTracker.getService();
if (config == null) throw new RuntimeException("Service 'Config' is not avalable!");
Manager manager = new Manager();
module = new MCSM_WebModule(manager);
module = new MCSM_WebModule();
logger.debug("Get service: {}", Webinterface.class);
serviceTracker = new ServiceTracker<>(context, Webinterface.class, null);
@@ -44,13 +42,13 @@ public class Activator implements BundleActivator, ServiceListener {
String passcode = config.getString("asys.mcsmanager.passcode", "testpasscode");
logger.debug("Start server manager: {}:{}", host, port);
serverManager = new asys.mcsmanager.server.Server();
serverManager.start(host, port, passcode, manager);
serverManager.start(host, port, passcode);
host = config.getString("asys.mcsmanager.webconsole.host", "127.0.0.1");
port = config.getInt("asys.mcsmanager.webconsole.port", 8770);
logger.debug("Start webconsole server: {}:{}", host, port);
webconsoleServer = new asys.mcsmanager.websocket.Server();
webconsoleServer.start(host, port, manager);
webconsoleServer.start(host, port);
serviceConfigTracker.close();
}
@@ -82,7 +80,6 @@ public class Activator implements BundleActivator, ServiceListener {
logger.debug("service not found =(");
}
} catch (InterruptedException ignore) {
// ignore
}
}
}

View File

@@ -27,11 +27,6 @@ public class MCSM_WebModule extends WebModule {
private final String MODULE_URL = "/"+MODULE_NAME;
private final Pattern URL_PATTERN_JS = Pattern.compile(MODULE_URL+"/(\\w+)\\.js");
private final Pattern URL_PATTERN_CSS = Pattern.compile(MODULE_URL+"/(\\w+)\\.css");
private Manager manager;
MCSM_WebModule(Manager manager) {
this.manager = manager;
}
@Override
public String getName() {
@@ -89,7 +84,7 @@ public class MCSM_WebModule extends WebModule {
if (httpExchange.getRequestURI().getQuery() != null &&
!httpExchange.getRequestURI().getQuery().isEmpty()) {
Map<String, String> query = this.queryToMap(httpExchange.getRequestURI().getQuery());
ServerInfo serverInfo = manager.getInfo(query.get("clientid"));
ServerInfo serverInfo = Manager.getInstance().getServer(query.get("clientid"));
if (serverInfo == null) {
this.sendJson(httpExchange, "{}");
} else {
@@ -102,6 +97,6 @@ public class MCSM_WebModule extends WebModule {
}
private JsonElement serverList() {
return GSON.toJsonTree(manager.getServerList());
return GSON.toJsonTree(Manager.getInstance().getServerList());
}
}

View File

@@ -5,92 +5,73 @@
package asys.mcsmanager;
import asys.mcsmanager.packets.CS_ConsoleMessage;
import asys.mcsmanager.packets.CS_Ping;
import asys.mcsmanager.packets.SC_Command;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class Manager {
private Logger logger = LoggerFactory.getLogger(Manager.class);
private Map<String, ServerInfo> serversMap = new HashMap<>();
private Map<String, Channel> serverChannels = new HashMap<>();
private List<Channel> webconsoleListener = new ArrayList<>();
private static Manager instance = new Manager();
private Map<String, ServerInfo> serverMap = new HashMap<>();
private Map<String, List<Channel>> webconsoleListeners = new HashMap<>();
/**
* Добавляем в список ClientID
* @param clientId id сервера. Чувствителен к регистру
* @return <code>false</code>, если сервер с таким id уже имеется
*/
public boolean addClientId(String clientId, Channel channel) {
if (serversMap.containsKey(clientId)) {
return false;
}
logger.debug("addClientId: {}", clientId);
serversMap.put(clientId, new ServerInfo(clientId));
serverChannels.put(clientId, channel);
return true;
public static Manager getInstance() {
return instance;
}
public void removeClientId(String clientId) {
if (clientId != null) {
serverChannels.remove(clientId);
}
private Manager(){
}
public boolean sendCommand(String clientId, String command) {
/*
if (serverChannels.containsKey(clientId)) {
serverChannels.get(clientId).writeAndFlush(new SC_Command(command));
return true;
} else {
return false;
}
*/
//FIXME временный костыль
Channel channel = serverChannels.entrySet().iterator().next().getValue();
channel.writeAndFlush(new SC_Command(command));
return true;
public void addServer(String serverName, Channel channel) {
this.serverMap.put(serverName, new ServerInfo(serverName, channel));
}
/**
* Дополнить информация о сервере.
* Если сервер отсутствует в списке, информация будет потеряна.
* @param clientId id сервера. Чувствителен к регистру
* @param pingPacket
*/
public void putInfo(String clientId, CS_Ping pingPacket) {
if (!serversMap.containsKey(clientId)) return;
serversMap.get(clientId).putPing(pingPacket);
public boolean existsServer(String serverName) {
return this.serverMap.containsKey(serverName);
}
public ServerInfo getServer(String serverName) {
return this.serverMap.get(serverName);
}
public Set<String> getServerList() {
return serversMap.keySet();
return this.serverMap.keySet();
}
public ServerInfo getInfo(String clientId) {
return serversMap.get(clientId);
public void removeServer(String serverName) {
this.serverMap.remove(serverName);
if (this.webconsoleListeners.containsKey(serverName)) {
this.webconsoleListeners.get(serverName).forEach(Channel::close);
this.webconsoleListeners.remove(serverName);
}
}
public void addWebConsoleListener(Channel channel) {
this.webconsoleListener.add(channel);
public void addWebconsoleListener(String serverName, Channel channel) {
this.webconsoleListeners
.computeIfAbsent(serverName, v -> new ArrayList<>())
.add(channel);
}
public void removeWebConsoleListener(Channel channel) {
this.webconsoleListener.remove(channel);
public void removeWebconsoleListener(String serverName, Channel channel) {
this.webconsoleListeners.get(serverName).remove(channel);
}
public void sendBroadcastToWebConsoleListeners(CS_ConsoleMessage message) {
for (Channel channel : this.webconsoleListener) {
channel.writeAndFlush(new TextWebSocketFrame(String.format(
"[L:%d] %s",
message.getLevel(),
message.getMessage()
)));
public void broadcastConsoleMessage(String serverName, CS_ConsoleMessage packet) {
if (this.webconsoleListeners.containsKey(serverName)) {
this.webconsoleListeners.get(serverName).forEach(channel -> {
channel.writeAndFlush(new TextWebSocketFrame(String.format(
"[L:%d] %s",
packet.getLevel(),
packet.getMessage()
)));
});
}
}
public void sendCommand(String serverName, String command) {
if (this.serverMap.containsKey(serverName)) {
this.serverMap.get(serverName).getChannel().writeAndFlush(new SC_Command(command));
}
}
}

View File

@@ -5,6 +5,7 @@
package asys.mcsmanager;
import asys.mcsmanager.packets.CS_Ping;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,18 +15,23 @@ import java.util.LinkedList;
public class ServerInfo {
private final Logger logger = LoggerFactory.getLogger(ServerInfo.class);
private final String name;
private Channel channel;
private int lastOnline;
private Deque<CS_Ping> pingDeque;
private Deque<CS_Ping> pingDeque = new LinkedList<>();
public ServerInfo(String name) {
public ServerInfo(String name, Channel channel) {
this.name = name;
this.pingDeque = new LinkedList<>();
this.channel = channel;
}
public String getName() {
return name;
}
public Channel getChannel() {
return channel;
}
public int getLastOnline() {
return lastOnline;
}

View File

@@ -4,7 +4,6 @@
*/
package asys.mcsmanager.server;
import asys.mcsmanager.Manager;
import asys.mcsmanager.packets.CS_Ping;
import asys.mcsmanager.packets.Packet;
import asys.mcsmanager.packets.codec.PacketDecoder;
@@ -25,11 +24,9 @@ public class Server {
);
private EventLoopGroup bossGroup, workerGroup;
static String passcode;
static Manager manager;
public void start(String host, int port, String passcode, Manager manager) {
public void start(String host, int port, String passcode) {
Server.passcode = passcode;
Server.manager = manager;
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();

View File

@@ -4,6 +4,7 @@
*/
package asys.mcsmanager.server;
import asys.mcsmanager.Manager;
import asys.mcsmanager.packets.*;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
@@ -16,7 +17,6 @@ import java.util.Map;
import static asys.mcsmanager.packets.codec.Params.KNOWN_HANDLERS;
import static asys.mcsmanager.packets.codec.Params.KNOWN_PACKETS;
import static asys.mcsmanager.server.Server.manager;
class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacketHandler {
private static final BiMap<Integer, Class<? extends Packet>> handshakePackets = ImmutableBiMap.of(
@@ -58,7 +58,7 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke
@Override
public void channelInactive(ChannelHandlerContext context) throws Exception {
manager.removeClientId(context.channel().attr(CLIENTID).get());
Manager.getInstance().removeServer(context.channel().attr(CLIENTID).get());
super.channelInactive(context);
}
@@ -69,7 +69,7 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke
} else if (packet.getClass() == CS_Ping.class) {
handleCSPing((CS_Ping) packet, context);
} else if (packet.getClass() == CS_ConsoleMessage.class) {
handleCSConsoleMessage((CS_ConsoleMessage) packet);
handleCSConsoleMessage(context.channel().attr(CLIENTID).get(), (CS_ConsoleMessage) packet);
}
}
@@ -78,18 +78,18 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke
try {
context.channel().writeAndFlush(HandshakeResult.INVALID_PASSCODE).sync().channel().close();
} catch (InterruptedException ignore) {
// ignore
}
return;
}
if (!manager.addClientId(packet.getClientId(), context.channel())) {
if (Manager.getInstance().existsServer(packet.getClientId())) {
try {
context.channel().writeAndFlush(HandshakeResult.CLIENTID_EXISTS).sync().channel().close();
} catch (InterruptedException ignore) {
// ignore
}
return;
} else {
Manager.getInstance().addServer(packet.getClientId(), context.channel());
}
context.channel().write(HandshakeResult.OK);
@@ -100,10 +100,10 @@ class ServerPacketHandler extends ChannelInboundHandlerAdapter implements IPacke
}
private void handleCSPing(CS_Ping packet, ChannelHandlerContext context) {
manager.putInfo(context.channel().attr(CLIENTID).get(), packet);
Manager.getInstance().getServer(context.channel().attr(CLIENTID).get()).putPing(packet);
}
private void handleCSConsoleMessage(CS_ConsoleMessage packet) {
manager.sendBroadcastToWebConsoleListeners(packet);
private void handleCSConsoleMessage(String serverName, CS_ConsoleMessage packet) {
Manager.getInstance().broadcastConsoleMessage(serverName, packet);
}
}

View File

@@ -4,36 +4,35 @@
*/
package asys.mcsmanager.websocket;
import asys.mcsmanager.Manager;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static asys.mcsmanager.websocket.Server.manager;
public class FrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
private static final AttributeKey<String> WC_SERVERNAME = AttributeKey.valueOf("WC_SERVERNAME");
private final Logger logger = LoggerFactory.getLogger(FrameHandler.class);
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
manager.addWebConsoleListener(ctx.channel());
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
manager.removeWebConsoleListener(ctx.channel());
Manager.getInstance().removeWebconsoleListener(ctx.channel().attr(WC_SERVERNAME).get(), ctx.channel());
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
if (frame instanceof TextWebSocketFrame) {
String requestText = ((TextWebSocketFrame)frame).text();
if (requestText.startsWith(":")) {
//FIXME убрать костыли
manager.sendCommand(null, requestText.substring(1));
if (requestText.startsWith("]")) {
String serverName = requestText.substring(1);
ctx.channel().attr(WC_SERVERNAME).set(serverName);
Manager.getInstance().addWebconsoleListener(serverName, ctx.channel());
} else if (requestText.startsWith(":")) {
String command = requestText.substring(1);
Manager.getInstance().sendCommand(ctx.channel().attr(WC_SERVERNAME).get(), command);
}
} else {
logger.warn("unsupport frame type: {}", frame.getClass().getName());

View File

@@ -4,7 +4,6 @@
*/
package asys.mcsmanager.websocket;
import asys.mcsmanager.Manager;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
@@ -16,11 +15,9 @@ import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
public class Server {
static Manager manager;
private EventLoopGroup bossGroup, workerGroup;
public void start(String host, int port, Manager manager) {
Server.manager = manager;
public void start(String host, int port) {
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();

View File

@@ -142,12 +142,15 @@ var ScrollingContent = React.createClass({
var WebConsole = React.createClass({
ws: null,
connect: function(){
connect: function(serverName){
if (this.ws !== null) return;
var _this = this;
this.ws = new WebSocket("ws://127.0.0.1:8770"); //FIXME указывать ip:port из настроек
this.ws.onopen = function(){ console.debug('WS: open...'); };
this.ws.onopen = function(){
console.debug('WS: open...');
_this.ws.send(']'+serverName);
};
this.ws.onclose = function(){ console.debug('WS: close...'); };
this.ws.onerror = function(e){ console.debug('WS: error'); console.error(e); };
this.ws.onmessage = function(event){
@@ -195,7 +198,7 @@ var WebConsole = React.createClass({
var ServerInfo = React.createClass({
tabStateWebConsole: function(state) {
if (state === 1) {
this.refs.webconsole.connect();
this.refs.webconsole.connect(this.state.title);
this.refs.webconsole.focusInput();
}
},
@@ -211,7 +214,7 @@ var ServerInfo = React.createClass({
return(
ce('div', null,
ce('h2', {style: {'margin-top': '0px'}}, this.state.title),
ce(Tabs, {tabs: ['Онлайн', 'Консоль'], stateCallback: this.tabStateWebConsole},
ce(Tabs, {tabs: ['Онлайн', 'Консоль'], stateCallback: this.tabStateWebConsole, ref: 'tabs'},
ce(NvLineChart, {datum: [{
key: 'Online players',
color: '#37d668',
@@ -223,6 +226,13 @@ var ServerInfo = React.createClass({
)
)
}
},
componentWillUpdate: function(nextProps, nextState) {
if (this.state.title === null) return;
if (this.state.title !== nextState.title) {
this.refs.webconsole.disconnect();
this.refs.tabs.setState({activeTab: 0});
}
}
});