using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; using Pluto.Registration; using System.Reflection; using Yaulw.Thread; namespace RegistrationServer { public partial class RegistrationService : ServiceBase { #region Internal Consts // Threshold used to determine if system just rebooted, and service just // started. We need to wait a little because we need to make sure the Database Server is up internal const int NUMBER_OF_MINUTES_SERVICE_UP_THRESHOLD = 5; internal const int NUMBER_OF_MINUTES_SYSTEM_UP_THRESHOLD = 10; #endregion #region Internal Statics /// /// Service Start Time /// internal static DateTime ServiceStartDT = DateTime.MinValue; #endregion #region Private Members /// /// Check if this is the first time the network got enabled /// private bool FirstTimeNetworkConnected = true; /// /// Was the Channel Activated? /// private bool ROHasBeenActivated = false; /// /// Did a System Reboot just occur? /// private bool SystemJustStarted_SoUseServiceThreshold = false; private bool ServiceUpTimeReached = false; private bool SystemThresholdNotReached = true; /// /// Is the Logger Setup? /// private bool LoggerIsSetup = false; /// /// Our Connectivity Timer Thread /// private SingleThreadTimer connectivityTimer = null; /// /// If for some reason the network goes down, we should re-initialize /// everything as if nothing happened /// private bool NetworkOutageDetected = false; #endregion #region Construction /// /// Singleton Instance of the Service /// internal static RegistrationService s_RegistrationService = null; /// /// Constructor /// public RegistrationService() { InitializeComponent(); // Log to the Application System Log this.EventLog.Log = "Application"; s_RegistrationService = this; // Handle all unexpected errors AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); } /// /// Unhandled Domain Exception /// /// /// static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Exception ex = (Exception)e.ExceptionObject; String msg = "UnhandledException Occured: " + ex.Message + "\n\n" + ex.InnerException.Message; AppLogError(msg); } #endregion #region Windows Event Logging internal static void AppLogError(string errMsg) { s_RegistrationService.EventLog.WriteEntry(errMsg, EventLogEntryType.Error); if (Registration.Logger != null) Registration.Logger.Error(errMsg); } internal static void AppLogInfo(string infoMsg) { s_RegistrationService.EventLog.WriteEntry(infoMsg, EventLogEntryType.Information); if (Registration.Logger != null) Registration.Logger.Info(infoMsg); } internal static void AppLogWarning(string warnMsg) { s_RegistrationService.EventLog.WriteEntry(warnMsg, EventLogEntryType.Warning); if (Registration.Logger != null) Registration.Logger.Info(warnMsg); } #endregion // usefull for debugging Service in visual studio #if DEBUG internal void DebugStart(string[] args) { DEBUGGING_ONLY.DEBUGSTEP_INTO(); OnStart(args); } #endif #region System / Service Up Time /// /// Get Service UpTime /// /// private TimeSpan GetServiceUpTime() { return (DateTime.Now - ServiceStartDT); } /// /// For Service Handling, see if the system has been up long enough /// Once it becomes true, it will always return true /// private bool HasServiceBeenUpLongEnough() { if (!ServiceUpTimeReached) ServiceUpTimeReached = GetServiceUpTime().TotalMinutes >= NUMBER_OF_MINUTES_SERVICE_UP_THRESHOLD; return ServiceUpTimeReached; } /// /// For Error Handling, see if the system has been up till this threshold /// Once it becomes false, will always return false /// /// private bool IsSystemBeenUpWithinMaxThreshold() { if (SystemThresholdNotReached) SystemThresholdNotReached = (Yaulw.Installer.Common.GetSystemUpTime().TotalMinutes <= NUMBER_OF_MINUTES_SYSTEM_UP_THRESHOLD); return SystemThresholdNotReached; } #endregion /// /// Service Start-Up /// /// protected override void OnStart(string[] args) { try { // Service Started // System just started, so make sure that service is timing out a little to make // sure everything is up and running SystemJustStarted_SoUseServiceThreshold = IsSystemBeenUpWithinMaxThreshold(); AppLogInfo(String.Format("MSLMobile Gateway Service started up at: {0}. System Reboot detected: {1}", DateTime.Now.ToLongTimeString(), SystemJustStarted_SoUseServiceThreshold)); ServiceStartDT = DateTime.Now; // Internet / Network Connectivity Timer // * Ran into issues when computer is rebooted, we need certain things working before the service can start, // * therefore decided to put DataStore Read(), AutoUpdate, RO Connection into a Connection Timer, // that keeps checking every 10 seconds if we have network connectivity * Enables / Disables RO Channels as needed, just in case * AppLogInfo("MSLMobile Gateway Service starting connectivity timer."); connectivityTimer = new SingleThreadTimer(ConnectivityHandler, (uint)TimeSpan.FromSeconds(10).TotalMilliseconds, true); } catch (Exception e) { AppLogError(String.Format("Service OnStart() Error thrown. Fatal. Can't continue: {0}", e.Message)); this.Stop(); } } #region Private Connectivity Timed Thread (runs every 10 Seconds) /// /// Single Threaded Connectivity Handler (Handles Auto-Update, ReadInDataStore(), and RO Channels) /// /// /// private void ConnectivityHandler(object sender, Yaulw.Thread.SingleThreadTimer.STElapsedEventArgs e) { // System just started, we won't do anyting in here until our // Service Startup 'NUMBER_OF_MINUTES_SERVICE_UP_THRESHOLD' timeout of is reached if (SystemJustStarted_SoUseServiceThreshold && !HasServiceBeenUpLongEnough()) return; // Check Connectivity, // ~withouth basic networking being up there is nothing really to do here bool bHasConnection = Yaulw.Net.IPHostHelper.HasConnection(); if (!bHasConnection) { DeactivateChannelIfActivated(true); return; } // Always set up the Logger First (Needed for all subsequent DB Calls) if (!LoggerIsSetup) { AppLogInfo("Setting up Logger"); Registration.Setup_Logger(); LoggerIsSetup = true; } // * Check to Make sure the System has been up long enough * // Calling VerifyConnectivity() requires SQL Server to be up // and running and hence, we can't do it right at service start when the // server just rebooted, we wait 5 of service uptime to make sure after a server reboot, // that the services needed are up if (FirstTimeNetworkConnected) { AppLogInfo("System has been up long enough. Starting Initialization."); // Make sure we can connect to the Database try { if (!DataAccessLayer.VerifyConnectivity()) { AppLogError("Cannot verify SQL Server Credentials upon initialization. Service must be stopped."); this.Stop(); } else { AppLogInfo("Success. SQL Server Credentials could connect upon initialization."); } } catch (Exception ex) { AppLogError(String.Format("Cannot verify SQL Server Credentials upon initialization '{0}'. Service must be stopped.", ex.Message)); this.Stop(); } FirstTimeNetworkConnected = false; return; } // We have a valid SQL Server Connection, hence we can continue if (!FirstTimeNetworkConnected) { // If Network disconnection occured, we should try to re-read in everything, // to make sure we are doing the best we can if (NetworkOutageDetected) { AppLogInfo("Network Outage Detected - Trying to retry connecting to SQL Server"); // Make sure we can connect to the Database try { if (!DataAccessLayer.VerifyConnectivity()) AppLogError("Cannot verify SQL Server Credentials after Network Outage."); else AppLogInfo("Success. SQL Server Credentials could connect after Networ Outage."); } catch (Exception ex) { AppLogError(String.Format("Cannot verify SQL Server Credentials after Network Outage '{0}'.", ex.Message)); } } // Activate the RO Channel ActivateChannelIfNotActivated(); } } #endregion #region Private RO Channel Helpers /// /// Activate all Channel /// private void ActivateChannelIfNotActivated() { if (!ROHasBeenActivated) { SetPort(Configuration.Port); ServerChannel.Activate(); ROHasBeenActivated = true; AppLogInfo(String.Format("MSLMobile Gateway Service Channel Activated on Port:{0}.", Configuration.Port)); NetworkOutageDetected = false; } } /// /// Deactivate RO Channel /// private void DeactivateChannelIfActivated(bool bNetworkOutage) { if (ROHasBeenActivated) { ServerChannel.Deactivate(); ROHasBeenActivated = false; AppLogInfo("MSLMobile Gateway Service Channel Deactivated."); NetworkOutageDetected = bNetworkOutage; } } #endregion // usefull for debugging Service in visual studio #if DEBUG internal void DebugStop() { OnStop(); } #endif /// /// SCM OnStop() call-in /// protected override void OnStop() { try { // First stop the internal connectivity timer if (connectivityTimer != null) { AppLogInfo("MSLMobile Gateway Service connectivity timer stopped."); connectivityTimer.Stop(); } DeactivateChannelIfActivated(false); AppLogInfo("End DeactivateChannelIfActivated."); } catch (Exception) { /* ignore */ } } } }