using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Serialization.Formatters;
using System.Text;
using System.Threading;
using SuperSocket.Common;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Config;
using SuperSocket.SocketBase.Logging;
using SuperSocket.SocketBase.Metadata;
using SuperSocket.SocketBase.Provider;
using SuperSocket.SocketEngine.Configuration;
namespace SuperSocket.SocketEngine
{
///
/// SuperSocket default bootstrap
///
public partial class DefaultBootstrap : IBootstrap, ILoggerProvider, IDisposable
{
private List m_AppServers;
private IWorkItem m_ServerManager;
///
/// Indicates whether the bootstrap is initialized
///
private bool m_Initialized = false;
///
/// Global configuration
///
private IConfigurationSource m_Config;
///
/// Global log
///
private ILog m_GlobalLog;
///
/// Gets the bootstrap logger.
///
ILog ILoggerProvider.Logger
{
get { return m_GlobalLog; }
}
///
/// Gets the log factory.
///
protected ILogFactory LogFactory { get; private set; }
///
/// Gets all the app servers running in this bootstrap
///
public IEnumerable AppServers
{
get { return m_AppServers; }
}
private readonly IRootConfig m_RootConfig;
///
/// Gets the config.
///
public IRootConfig Config
{
get
{
if (m_Config != null)
return m_Config;
return m_RootConfig;
}
}
///
/// Gets the startup config file.
///
public string StartupConfigFile { get; private set; }
///
/// Gets the class.
///
public IPerformanceMonitor PerfMonitor { get { return m_PerfMonitor; } }
private PerformanceMonitor m_PerfMonitor;
private readonly string m_BaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
///
/// Gets the base directory.
///
///
/// The base directory.
///
public string BaseDirectory
{
get
{
return m_BaseDirectory;
}
}
partial void SetDefaultCulture(IRootConfig rootConfig);
///
/// Initializes a new instance of the class.
///
/// The app servers.
public DefaultBootstrap(IEnumerable appServers)
: this(new RootConfig(), appServers, new Log4NetLogFactory())
{
}
///
/// Initializes a new instance of the class.
///
/// The root config.
/// The app servers.
public DefaultBootstrap(IRootConfig rootConfig, IEnumerable appServers)
: this(rootConfig, appServers, new Log4NetLogFactory())
{
}
///
/// Initializes a new instance of the class.
///
/// The root config.
/// The app servers.
/// The log factory.
public DefaultBootstrap(IRootConfig rootConfig, IEnumerable appServers, ILogFactory logFactory)
{
if (rootConfig == null)
throw new ArgumentNullException("rootConfig");
if (appServers == null)
throw new ArgumentNullException("appServers");
if(!appServers.Any())
throw new ArgumentException("appServers must have one item at least", "appServers");
if (logFactory == null)
throw new ArgumentNullException("logFactory");
m_RootConfig = rootConfig;
SetDefaultCulture(rootConfig);
m_AppServers = appServers.ToList();
m_GlobalLog = logFactory.GetLog(this.GetType().Name);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
if (!rootConfig.DisablePerformanceDataCollector)
{
m_PerfMonitor = new PerformanceMonitor(rootConfig, m_AppServers, null, logFactory);
if (m_GlobalLog.IsDebugEnabled)
m_GlobalLog.Debug("The PerformanceMonitor has been initialized!");
}
if (m_GlobalLog.IsDebugEnabled)
m_GlobalLog.Debug("The Bootstrap has been initialized!");
m_Initialized = true;
}
///
/// Initializes a new instance of the class.
///
/// The config.
public DefaultBootstrap(IConfigurationSource config)
{
if (config == null)
throw new ArgumentNullException("config");
SetDefaultCulture(config);
var fileConfigSource = config as ConfigurationSection;
if (fileConfigSource != null)
StartupConfigFile = fileConfigSource.GetConfigSource();
m_Config = config;
AppDomain.CurrentDomain.SetData("Bootstrap", this);
}
///
/// Initializes a new instance of the class.
///
/// The config.
/// The startup config file.
public DefaultBootstrap(IConfigurationSource config, string startupConfigFile)
{
if (config == null)
throw new ArgumentNullException("config");
SetDefaultCulture(config);
if (!string.IsNullOrEmpty(startupConfigFile))
StartupConfigFile = startupConfigFile;
m_Config = config;
AppDomain.CurrentDomain.SetData("Bootstrap", this);
}
///
/// Creates the work item instance.
///
/// Name of the service type.
/// The server status metadata.
///
protected virtual IWorkItem CreateWorkItemInstance(string serviceTypeName, StatusInfoAttribute[] serverStatusMetadata)
{
var serviceType = Type.GetType(serviceTypeName, true);
return Activator.CreateInstance(serviceType) as IWorkItem;
}
internal virtual bool SetupWorkItemInstance(IWorkItem workItem, WorkItemFactoryInfo factoryInfo)
{
try
{
//Share AppDomain AppServers also share same socket server factory and log factory instances
factoryInfo.SocketServerFactory.ExportFactory.EnsureInstance();
factoryInfo.LogFactory.ExportFactory.EnsureInstance();
}
catch (Exception e)
{
if (m_GlobalLog.IsErrorEnabled)
m_GlobalLog.Error(e);
return false;
}
return workItem.Setup(this, factoryInfo.Config, factoryInfo.ProviderFactories.ToArray());
}
///
/// Gets the work item factory info loader.
///
/// The config.
/// The log factory.
///
internal virtual WorkItemFactoryInfoLoader GetWorkItemFactoryInfoLoader(IConfigurationSource config, ILogFactory logFactory)
{
return new WorkItemFactoryInfoLoader(config, logFactory);
}
///
/// Initializes the bootstrap with a listen endpoint replacement dictionary
///
/// The listen end point replacement.
///
public virtual bool Initialize(IDictionary listenEndPointReplacement)
{
return Initialize((c) => ReplaceListenEndPoint(c, listenEndPointReplacement));
}
private IServerConfig ReplaceListenEndPoint(IServerConfig serverConfig, IDictionary listenEndPointReplacement)
{
var config = new ServerConfig(serverConfig);
if (serverConfig.Port > 0)
{
var endPointKey = serverConfig.Name + "_" + serverConfig.Port;
IPEndPoint instanceEndpoint;
if(!listenEndPointReplacement.TryGetValue(endPointKey, out instanceEndpoint))
{
throw new Exception(string.Format("Failed to find Input Endpoint configuration {0}!", endPointKey));
}
config.Ip = instanceEndpoint.Address.ToString();
config.Port = instanceEndpoint.Port;
}
if (config.Listeners != null && config.Listeners.Any())
{
var listeners = config.Listeners.ToArray();
for (var i = 0; i < listeners.Length; i++)
{
var listener = (ListenerConfig)listeners[i];
var endPointKey = serverConfig.Name + "_" + listener.Port;
IPEndPoint instanceEndpoint;
if (!listenEndPointReplacement.TryGetValue(endPointKey, out instanceEndpoint))
{
throw new Exception(string.Format("Failed to find Input Endpoint configuration {0}!", endPointKey));
}
listener.Ip = instanceEndpoint.Address.ToString();
listener.Port = instanceEndpoint.Port;
}
config.Listeners = listeners;
}
return config;
}
private IWorkItem InitializeAndSetupWorkItem(WorkItemFactoryInfo factoryInfo)
{
IWorkItem appServer;
try
{
appServer = CreateWorkItemInstance(factoryInfo.ServerType, factoryInfo.StatusInfoMetadata);
if (m_GlobalLog.IsDebugEnabled)
m_GlobalLog.DebugFormat("The server instance {0} has been created!", factoryInfo.Config.Name);
}
catch (Exception e)
{
if (m_GlobalLog.IsErrorEnabled)
m_GlobalLog.Error(string.Format("Failed to create server instance {0}!", factoryInfo.Config.Name), e);
return null;
}
var exceptionSource = appServer as IExceptionSource;
if (exceptionSource != null)
exceptionSource.ExceptionThrown += new EventHandler(exceptionSource_ExceptionThrown);
var setupResult = false;
try
{
setupResult = SetupWorkItemInstance(appServer, factoryInfo);
if (m_GlobalLog.IsDebugEnabled)
m_GlobalLog.DebugFormat("The server instance {0} has been initialized!", appServer.Name);
}
catch (Exception e)
{
m_GlobalLog.Error(e);
setupResult = false;
}
if (!setupResult)
{
if (m_GlobalLog.IsErrorEnabled)
m_GlobalLog.Error("Failed to setup server instance!");
return null;
}
return appServer;
}
///
/// Initializes the bootstrap with the configuration, config resolver and log factory.
///
/// The server config resolver.
/// The log factory.
///
public virtual bool Initialize(Func serverConfigResolver, ILogFactory logFactory)
{
if (m_Initialized)
throw new Exception("The server had been initialized already, you cannot initialize it again!");
if (logFactory != null && !string.IsNullOrEmpty(m_Config.LogFactory))
{
throw new ArgumentException("You cannot pass in a logFactory parameter, if you have configured a root log factory.", "logFactory");
}
IEnumerable workItemFactories;
using (var factoryInfoLoader = GetWorkItemFactoryInfoLoader(m_Config, logFactory))
{
var bootstrapLogFactory = factoryInfoLoader.GetBootstrapLogFactory();
logFactory = bootstrapLogFactory.ExportFactory.CreateExport();
LogFactory = logFactory;
m_GlobalLog = logFactory.GetLog(this.GetType().Name);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
try
{
workItemFactories = factoryInfoLoader.LoadResult(serverConfigResolver);
}
catch (Exception e)
{
if (m_GlobalLog.IsErrorEnabled)
m_GlobalLog.Error(e);
return false;
}
}
m_AppServers = new List(m_Config.Servers.Count());
IWorkItem serverManager = null;
//Initialize servers
foreach (var factoryInfo in workItemFactories)
{
IWorkItem appServer = InitializeAndSetupWorkItem(factoryInfo);
if (appServer == null)
return false;
if (factoryInfo.IsServerManager)
serverManager = appServer;
else if (!(appServer is IsolationAppServer))//No isolation
{
//In isolation mode, cannot check whether is server manager in the factory info loader
if (TypeValidator.IsServerManagerType(appServer.GetType()))
serverManager = appServer;
}
m_AppServers.Add(appServer);
}
if (serverManager != null)
m_ServerManager = serverManager;
if (!m_Config.DisablePerformanceDataCollector)
{
m_PerfMonitor = new PerformanceMonitor(m_Config, m_AppServers, serverManager, logFactory);
if (m_GlobalLog.IsDebugEnabled)
m_GlobalLog.Debug("The PerformanceMonitor has been initialized!");
}
if (m_GlobalLog.IsDebugEnabled)
m_GlobalLog.Debug("The Bootstrap has been initialized!");
try
{
RegisterRemotingService();
}
catch (Exception e)
{
if (m_GlobalLog.IsErrorEnabled)
m_GlobalLog.Error("Failed to register remoting access service!", e);
return false;
}
m_Initialized = true;
return true;
}
void exceptionSource_ExceptionThrown(object sender, ErrorEventArgs e)
{
m_GlobalLog.Error(string.Format("The server {0} threw an exception.", ((IWorkItemBase)sender).Name), e.Exception);
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
m_GlobalLog.Error("The process crashed for an unhandled exception!", (Exception)e.ExceptionObject);
}
///
/// Initializes the bootstrap with the configuration and config resolver.
///
/// The server config resolver.
///
public virtual bool Initialize(Func serverConfigResolver)
{
return Initialize(serverConfigResolver, null);
}
///
/// Initializes the bootstrap with the configuration
///
/// The log factory.
///
public virtual bool Initialize(ILogFactory logFactory)
{
return Initialize(c => c, logFactory);
}
///
/// Initializes the bootstrap with the configuration
///
///
public virtual bool Initialize()
{
return Initialize(c => c);
}
///
/// Starts this bootstrap.
///
///
public StartResult Start()
{
if (!m_Initialized)
{
if (m_GlobalLog.IsErrorEnabled)
m_GlobalLog.Error("You cannot invoke method Start() before initializing!");
return StartResult.Failed;
}
var result = StartResult.None;
var succeeded = 0;
foreach (var server in m_AppServers)
{
if (!server.Start())
{
if (m_GlobalLog.IsErrorEnabled)
m_GlobalLog.InfoFormat("The server instance {0} has failed to be started!", server.Name);
}
else
{
succeeded++;
if (Config.Isolation != IsolationMode.None)
{
if (m_GlobalLog.IsInfoEnabled)
m_GlobalLog.InfoFormat("The server instance {0} has been started!", server.Name);
}
}
}
if (m_AppServers.Any())
{
if (m_AppServers.Count == succeeded)
result = StartResult.Success;
else if (succeeded == 0)
result = StartResult.Failed;
else
result = StartResult.PartialSuccess;
}
if (m_PerfMonitor != null)
{
m_PerfMonitor.Start();
if (m_GlobalLog.IsDebugEnabled)
m_GlobalLog.Debug("The PerformanceMonitor has been started!");
}
return result;
}
///
/// Stops this bootstrap.
///
public void Stop()
{
var servers = m_AppServers.ToArray();
if (servers.Any(s => s.Config != null && s.Config.StartupOrder != 0))
{
Array.Reverse(servers);
}
foreach (var server in servers)
{
if (server.State == ServerState.Running)
{
server.Stop();
if (Config.Isolation != IsolationMode.None)
{
if (m_GlobalLog.IsInfoEnabled)
m_GlobalLog.InfoFormat("The server instance {0} has been stopped!", server.Name);
}
}
}
if (m_PerfMonitor != null)
{
m_PerfMonitor.Stop();
if (m_GlobalLog.IsDebugEnabled)
m_GlobalLog.Debug("The PerformanceMonitor has been stoppped!");
}
}
///
/// Registers the bootstrap remoting access service.
///
protected virtual void RegisterRemotingService()
{
var bootstrapIpcPort = string.Format("SuperSocket.Bootstrap[{0}]", Math.Abs(AppDomain.CurrentDomain.BaseDirectory.TrimEnd(System.IO.Path.DirectorySeparatorChar).GetHashCode()));
var serverChannelName = "Bootstrap";
var serverChannel = ChannelServices.RegisteredChannels.FirstOrDefault(c => c.ChannelName == serverChannelName);
if (serverChannel != null)
ChannelServices.UnregisterChannel(serverChannel);
serverChannel = new IpcServerChannel(serverChannelName, bootstrapIpcPort, new BinaryServerFormatterSinkProvider { TypeFilterLevel = TypeFilterLevel.Full });
ChannelServices.RegisterChannel(serverChannel, false);
AppDomain.CurrentDomain.SetData("BootstrapIpcPort", bootstrapIpcPort);
var bootstrapProxyType = typeof(RemoteBootstrapProxy);
if (!RemotingConfiguration.GetRegisteredWellKnownServiceTypes().Any(s => s.ObjectType == bootstrapProxyType))
RemotingConfiguration.RegisterWellKnownServiceType(bootstrapProxyType, "Bootstrap.rem", WellKnownObjectMode.Singleton);
}
///
/// Releases unmanaged and - optionally - managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
AppDomain.CurrentDomain.UnhandledException -= new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
}
///
/// Releases unmanaged and - optionally - managed resources.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void ResetPerfMoniter()
{
if (m_PerfMonitor != null)
{
m_PerfMonitor.Stop();
m_PerfMonitor.Dispose();
m_PerfMonitor = null;
}
m_PerfMonitor = new PerformanceMonitor(m_Config, m_AppServers, m_ServerManager, LogFactory);
m_PerfMonitor.Start();
if (m_GlobalLog.IsDebugEnabled)
m_GlobalLog.Debug("The PerformanceMonitor has been reset for new server has been added!");
}
}
}