/*
* Created by Andrey Cherkashin (acherkashin)
* http://acherkashin.me
*
* License
* Copyright (c) 2015 Andrey Cherkashin
* The project released under the MIT license: http://opensource.org/licenses/MIT
*/
package ragefist.core.distribution;
import com.juniform.JUniform;
import com.juniform.JUniformObject;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import ragefist.Main;
import ragefist.core.environment.EnvironmentProcessor;
import ragefist.core.environment.EnvironmentProcessor.EnvironmentBuilder;
import ragefist.core.network.Connection;
import ragefist.core.network.Server;
import ragefist.core.network.SocketStrategyConsole;
import ragefist.core.network.SocketStrategyDefault;
import ragefist.core.network.SocketStrategyHTTP;
import ragefist.extension.Extension;
/**
*
* @author acherkashin
*/
public class DistributedServerController
{
// ---------------------------------------------------------------------- //
// PRIVATE
// ---------------------------------------------------------------------- //
private final List<Extension> _extensions = new ArrayList<>();
private final List<Thread> _environmentThreads = new ArrayList<>();
private final List<EnvironmentProcessor> _environments = new ArrayList<>();
private Thread _rpcServerThread;
private Server _rpcServer;
private Thread _consoleServerThread;
private Server _consoleServer;
private Thread _clientServerThread;
private Server _clientServer;
private Thread _httpClientServerThread;
private Server _httpClientServer;
private final DistributedServer _controlledServer;
private final DistributedServerGroup _controlledServerGroup;
private final DistributedServerControllerAPI _api;
private final DistributedServerConnector _connector;
private Thread _connectorThread;
private final Map<String,DistributedServerGroup> _serverGroupMap;
private final DistributedServerPacketHandler _rpcPacketHandler;
private final DistributedServerPacketHandler _consolePacketHandler;
private final DistributedServerPacketHandler _clientPacketHandler;
private final boolean _disableCrossEnvironmentInteraction;
private final boolean _isTestServer;
// ---------------------------------------------------------------------- //
// API
// This interface will be used in packet handlers. It gives us a possibility
// to hide functions like run() or start() from them.
// ---------------------------------------------------------------------- //
public final class DistributedServerControllerAPI
{
public final String getCurrentServerGroupCode() { return _controlledServerGroup.getCode(); }
public final int getCurrentServerId() { return _controlledServer.getId(); }
public final SelectionPolicy getSelectionPolicy() { return _controlledServer.getSelectionPolicy(); }
public final int getEnvironmentCount() { return _environments.size(); }
public final EnvironmentProcessor getEnvironmentById(int id) { return _environments.get(id); }
public final DistributedServerConnector getConnector() { return _connector; }
public final int getRPCConnectionsCount() { return _rpcServer.getConnectionsCount(); }
public final int getClientConnectionsCount() { return _clientServer.getConnectionsCount(); }
public final int getConsoleConnectionsCount() { return _consoleServer.getConnectionsCount(); }
public final boolean isCrossEnvironmentInteractionDisabled() { return _disableCrossEnvironmentInteraction; }
public final boolean isTestServer() { return _isTestServer; }
public final boolean sendMessageToClient(int id, JUniformObject data) {
Connection connection = _clientServer.getConnectionById(id);
if (connection != null) {
connection.sendPacket(data);
return true;
}
return false;
}
public final List<Extension> getExtensions() { return _extensions; }
}
// ---------------------------------------------------------------------- //
// BUILDER
// ---------------------------------------------------------------------- //
public static class DistributedServerControllerBuilder
{
// @required
public DistributedServerGroup controlledServerGroup;
public DistributedServer controlledServer;
public Map<String,DistributedServerGroup> serverGroupMap;
public boolean disableCrossEnvironmentInteraction = false;
public boolean isTestServer = false;
// @optional
public Class<? extends DistributedServerPacketHandler> rpcPacketHandlerClass;
public Class<? extends DistributedServerPacketHandler> consolePacketHandlerClass;
public Class<? extends DistributedServerPacketHandler> clientPacketHandlerClass;
protected void _checkBuildThrowExceptions() throws IllegalArgumentException {
if (controlledServerGroup == null) {
throw new IllegalArgumentException("DistributedServer.controlledServerGroup is null");
}
if (controlledServer == null) {
throw new IllegalArgumentException("DistributedServer.controlledServer is null");
}
if (serverGroupMap == null || serverGroupMap.isEmpty()) {
throw new IllegalArgumentException("DistributedServer.serverGroupMap is null or empty");
}
}
public DistributedServerController build() throws IllegalArgumentException {
_checkBuildThrowExceptions();
return new DistributedServerController(this);
}
}
private DistributedServerController(DistributedServerControllerBuilder builder) throws IllegalArgumentException {
_controlledServer = builder.controlledServer;
_controlledServerGroup = builder.controlledServerGroup;
_api = this.new DistributedServerControllerAPI();
_serverGroupMap = builder.serverGroupMap;
_connector = new DistributedServerConnector(_serverGroupMap);
_disableCrossEnvironmentInteraction = builder.disableCrossEnvironmentInteraction;
_isTestServer = builder.isTestServer;
try {
if (builder.clientPacketHandlerClass != null) {
_clientPacketHandler = builder.clientPacketHandlerClass.getConstructor(DistributedServerControllerAPI.class).newInstance(_api);
} else {
_clientPacketHandler = null;
}
if (builder.rpcPacketHandlerClass != null) {
_rpcPacketHandler = builder.rpcPacketHandlerClass.getConstructor(DistributedServerControllerAPI.class).newInstance(_api);
} else {
_rpcPacketHandler = null;
}
if (builder.consolePacketHandlerClass != null) {
_consolePacketHandler = builder.consolePacketHandlerClass.getConstructor(DistributedServerControllerAPI.class).newInstance(_api);
} else {
_consolePacketHandler = null;
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
throw new IllegalArgumentException(ex);
}
}
// ---------------------------------------------------------------------- //
// Interface
// ---------------------------------------------------------------------- //
public DistributedServerControllerAPI getAPI() {
return _api;
}
public void addExtension(Extension extension) {
_extensions.add(extension);
}
// ---------------------------------------------------------------------- //
// RUN
// ---------------------------------------------------------------------- //
public boolean start() {
// ------------------------ //
// CREATING ENVIRONMENTS
// ------------------------ //
// Environments builder
EnvironmentBuilder builder = new EnvironmentBuilder();
builder.luaConnectorFile = _controlledServer.getEnvironmentLuaConnectorFile();
builder.luaMainFile = _controlledServer.getEnvironmentLuaMainFile();
builder.serverControllerAPI = _api;
// Creating Lua environments in different threads
try {
for(int i = 0; i < _controlledServer.getEnvironmentCount(); i++) {
// Builder params
builder.id = i;
// Build & start
EnvironmentProcessor environment = builder.build();
_environments.add(environment);
Thread thread = new Thread(environment);
_environmentThreads.add(thread);
thread.start();
}
} catch(IllegalArgumentException ex) {
Logger.getLogger(DistributedServerController.class.getName()).log(Level.SEVERE, "Failed to create all the environments", ex);
return false;
}
// ------------------------ //
// SETTING UP SERVERS
// ------------------------ //
Server.ServerBuilder serverBuilder = new Server.ServerBuilder();
// RPC SERVER
serverBuilder.host = _controlledServer.getRPCHost();
serverBuilder.port = _controlledServer.getRPCPort();
serverBuilder.packetHandler = _rpcPacketHandler;
serverBuilder.packetPacker = JUniform.getPackerInstance(_controlledServer.getRPCPacketFormat());
serverBuilder.socketStrategy = SocketStrategyDefault.getInstance();
try {
_rpcServer = serverBuilder.build();
_rpcServer.open();
} catch(IllegalArgumentException | IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to build RPC server", ex);
return false;
}
_rpcServerThread = new Thread(_rpcServer);
_rpcServerThread.start();
// CONSOLE SERVER
serverBuilder.host = _controlledServer.getConsoleHost();
serverBuilder.port = _controlledServer.getConsolePort();
serverBuilder.packetHandler = _consolePacketHandler;
serverBuilder.packetPacker = JUniform.getPackerInstance(JUniform.PACKER_TYPE_STRING);
serverBuilder.socketStrategy = SocketStrategyConsole.getInstance();
try {
_consoleServer = serverBuilder.build();
_consoleServer.open();
} catch(IllegalArgumentException | IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to build CONSOLE server", ex);
return false;
}
_consoleServerThread = new Thread(_consoleServer);
_consoleServerThread.start();
// CLIENT SERVER
serverBuilder.host = _controlledServer.getClientHost();
serverBuilder.port = _controlledServer.getClientPort();
serverBuilder.packetHandler = _clientPacketHandler;
serverBuilder.packetPacker = JUniform.getPackerInstance(_controlledServer.getClientPacketFormat());
serverBuilder.socketStrategy = SocketStrategyDefault.getInstance();
try {
_clientServer = serverBuilder.build();
_clientServer.open();
} catch(IllegalArgumentException | IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to build CLIENT server", ex);
return false;
}
_clientServerThread = new Thread(_clientServer);
_clientServerThread.start();
// HTTP CLIENT SERVER
if (_controlledServer.getHTTPClientHost() != null &&
_controlledServer.getHTTPClientPort() != 0
) {
serverBuilder.host = _controlledServer.getHTTPClientHost();
serverBuilder.port = _controlledServer.getHTTPClientPort();
serverBuilder.packetHandler = _clientPacketHandler;
serverBuilder.packetPacker = JUniform.getPackerInstance(_controlledServer.getHTTPClientPacketFormat());
serverBuilder.socketStrategy = SocketStrategyHTTP.getInstance();
try {
_httpClientServer = serverBuilder.build();
_httpClientServer.open();
} catch(IllegalArgumentException | IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to build CLIENT server", ex);
return false;
}
_httpClientServerThread = new Thread(_httpClientServer);
_httpClientServerThread.start();
}
// ------------------------ //
// SETTING UP CONNECTOR
// ------------------------ //
_connectorThread = new Thread(_connector);
_connectorThread.start();
return true;
}
}