using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Config; using SuperSocket.SocketBase.Metadata; using SuperSocket.SocketBase.Provider; namespace SuperSocket.SocketEngine { abstract class IsolationAppServer : MarshalByRefObject, IWorkItem, IStatusInfoSource, IExceptionSource, IDisposable { protected const string WorkingDir = "AppRoot"; protected string ServerTypeName { get; private set; } protected IBootstrap Bootstrap { get; private set; } public IServerConfig Config { get; private set; } protected ProviderFactoryInfo[] Factories { get; private set; } protected IWorkItemBase AppServer { get; private set; } public string Name { get; private set; } private StatusInfoAttribute[] m_ServerStatusMetadata; private AutoResetEvent m_StopResetEvent = new AutoResetEvent(false); protected IsolationAppServer(string serverTypeName, StatusInfoAttribute[] serverStatusMetadata) { State = ServerState.NotInitialized; ServerTypeName = serverTypeName; m_ServerStatusMetadata = PrepareStatusMetadata(serverStatusMetadata); } /// <summary> /// Gets a value indicating whether [status metadata extended]. /// </summary> /// <value> /// <c>true</c> if [status metadata extended]; otherwise, <c>false</c>. /// </value> protected virtual bool StatusMetadataExtended { get { return false; } } private StatusInfoAttribute[] PrepareStatusMetadata(StatusInfoAttribute[] serverStatusMetadata) { if (!StatusMetadataExtended) return serverStatusMetadata; var additionalAttrs = this.GetType() .GetCustomAttributes(typeof(StatusInfoAttribute), true) .OfType<StatusInfoAttribute>() .ToArray(); if (additionalAttrs.Length == 0) return serverStatusMetadata; var list = serverStatusMetadata.ToList(); list.AddRange(additionalAttrs); return list.ToArray(); } protected AppDomain CreateHostAppDomain() { var currentDomain = AppDomain.CurrentDomain; var workingDir = Path.Combine(Path.Combine(currentDomain.BaseDirectory, WorkingDir), Name); if (!Directory.Exists(workingDir)) Directory.CreateDirectory(workingDir); var startupConfigFile = Bootstrap.StartupConfigFile; if (!string.IsNullOrEmpty(startupConfigFile)) { if (!Path.IsPathRooted(startupConfigFile)) startupConfigFile = Path.Combine(currentDomain.BaseDirectory, startupConfigFile); } var hostAppDomain = AppDomain.CreateDomain(Name, currentDomain.Evidence, new AppDomainSetup { ApplicationName = Name, ApplicationBase = workingDir, ConfigurationFile = startupConfigFile }); var assemblyImportType = typeof(AssemblyImport); hostAppDomain.CreateInstanceFrom(assemblyImportType.Assembly.CodeBase, assemblyImportType.FullName, true, BindingFlags.CreateInstance, null, new object[] { currentDomain.BaseDirectory }, null, new object[0]); return hostAppDomain; } public virtual bool Setup(IBootstrap bootstrap, IServerConfig config, ProviderFactoryInfo[] factories) { State = ServerState.Initializing; Name = config.Name; Bootstrap = bootstrap; Config = config; Factories = factories; State = ServerState.NotStarted; return true; } public virtual void ReportPotentialConfigChange(IServerConfig config) { Config = config; if (State != ServerState.Stopping && State != ServerState.NotStarted) return; var appServer = AppServer; if (appServer == null) return; appServer.ReportPotentialConfigChange(config); } protected abstract IWorkItemBase Start(); bool IWorkItemBase.Start() { State = ServerState.Starting; AppServer = Start(); if (AppServer != null) { State = ServerState.Running; return true; } else { State = ServerState.NotStarted; return false; } } public ServerState State { get; private set; } protected abstract void Stop(); void IWorkItemBase.Stop() { var appServer = AppServer; if (appServer == null) return; State = ServerState.Stopping; appServer.Stop(); Stop(); m_StopResetEvent.WaitOne(); } protected virtual void OnStopped() { State = ServerState.NotStarted; AppServer = null; m_StopResetEvent.Set(); } public int SessionCount { get { var appServer = AppServer; if (appServer == null) return 0; return appServer.SessionCount; } } public StatusInfoAttribute[] GetServerStatusMetadata() { return m_ServerStatusMetadata; } private StatusInfoCollection m_PrevStatus; private StatusInfoCollection m_StoppedStatus; private StatusInfoCollection GetStoppedStatus() { if (m_StoppedStatus == null) { m_StoppedStatus = new StatusInfoCollection(); m_StoppedStatus.Name = Name; m_StoppedStatus.Tag = Name; m_StoppedStatus[StatusInfoKeys.IsRunning] = false; m_StoppedStatus[StatusInfoKeys.MaxConnectionNumber] = Config.MaxConnectionNumber; if (m_PrevStatus != null) { m_StoppedStatus[StatusInfoKeys.Listeners] = m_PrevStatus[StatusInfoKeys.Listeners]; } } return m_StoppedStatus; } public virtual StatusInfoCollection CollectServerStatus(StatusInfoCollection nodeStatus) { var appServer = AppServer; if (appServer == null) { var stoppedStatus = GetStoppedStatus(); stoppedStatus.CollectedTime = DateTime.Now; return stoppedStatus; } var currentStatus = appServer.CollectServerStatus(nodeStatus); m_PrevStatus = currentStatus; return currentStatus; } /// <summary> /// Obtains a lifetime service object to control the lifetime policy for this instance. /// Return null, never expired /// </summary> /// <returns> /// An object of type <see cref="T:System.Runtime.Remoting.Lifetime.ILease" /> used to control the lifetime policy for this instance. This is the current lifetime service object for this instance if one exists; otherwise, a new lifetime service object initialized to the value of the <see cref="P:System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseManagerPollTime" /> property. /// </returns> /// <PermissionSet> /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="RemotingConfiguration, Infrastructure" /> /// </PermissionSet> public override object InitializeLifetimeService() { return null; } public void Dispose() { Dispose(true); // Use SupressFinalize in case a subclass // of this type implements a finalizer. GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { Stop(); } ~IsolationAppServer() { Dispose(false); } public event EventHandler<SuperSocket.Common.ErrorEventArgs> ExceptionThrown; protected void OnExceptionThrown(Exception exc) { var handler = ExceptionThrown; if (handler == null) return; handler(this, new SuperSocket.Common.ErrorEventArgs(exc)); } public void TransferSystemMessage(string messageType, object messageData) { var appServer = AppServer; if (appServer == null) OnExceptionThrown(new Exception("You cannot send system message to the instance who is not started.")); appServer.TransferSystemMessage(messageType, messageData); } } }