/// <summary> /// Disconnect from the KCD if required. /// </summary> private void DisconnectFromKcdIfNeeded() { if (!WantKcdConnected() && m_kws.InKcdConnectTree()) { WmSm.HandleKwsToDisconnect(m_kws); } }
/// <summary> /// Called when the login fails for some reason. /// </summary> public void HandleLoginFailure(Exception ex) { WmSm.LockNotif(); Debug.Assert(m_ks.LoginStatus == KwsLoginStatus.LoggingIn); // Update our login state. UpdateStateOnLogout(ex); // The events are out of sync. if (m_ks.LoginResult == KwsLoginResult.OOS) { // Request a rebuild. We have to delete both the cached events and // the user data. This is nasty. if (m_cd.MainStatus == KwsMainStatus.Good || m_cd.MainStatus == KwsMainStatus.RebuildRequired) { m_cd.MainStatus = KwsMainStatus.RebuildRequired; m_cd.RebuildFlags = KwsRebuildFlag.FlushKcdData | KwsRebuildFlag.FlushLocalData; } } // Make the workspace work offline if required. The core operations // may have already marked the workspace for removal. if (m_cd.CurrentTask == KwsTask.WorkOnline) { SetUserTask(KwsTask.WorkOffline); RequestTaskSwitch(KwsTask.WorkOffline, ex); } WmSm.UnlockNotif(); }
/// <summary> /// Connect to the KCD if required. /// </summary> private void ConnectToKcdIfNeeded() { if (WantKcdConnected() && !m_kws.InKcdConnectTree()) { WmSm.HandleKwsToConnect(m_kws); } }
///////////////////////////////////////////// // Interface methods for external parties. // ///////////////////////////////////////////// /// <summary> /// This method must be called by each application when it has started. /// </summary> public void OnAppStarted() { // The applications are no longer starting. if (m_cd.AppStatus != KwsAppStatus.Starting) { return; } // Not all applications have started. foreach (KwsApp app in m_kws.AppTree.Values) { if (app.AppStatus != KwsAppStatus.Started) { return; } } WmSm.LockNotif(); // All applications are started. m_cd.AppStatus = KwsAppStatus.Started; // Notify the listeners. WmSm.QueueNotif(new KwsSmNotifApp(m_kws, m_cd.AppStatus)); // Let the state machine sort it out. RequestRun("Applications started"); WmSm.UnlockNotif(); }
/// <summary> /// This method should be called when an unexpected failure occurs /// in the workspace. /// </summary> public void HandleMiscFailure(Exception ex) { WmSm.LockNotif(); KLogging.LogException(ex); // We cannot handle failures during task switches. We need the // task switches to succeed to recover from failures. if (m_taskSwitchFlag) { KBase.HandleException(ex, true); } // Increase the severity of the rebuild required if possible. if (m_cd.CurrentTask == KwsTask.Rebuild) { WorsenRebuild(KwsRebuildFlag.FlushKcdData | KwsRebuildFlag.FlushLocalData); } // Stop the workspace if required. SetUserTask(KwsTask.Stop); RequestTaskSwitch(KwsTask.Stop, ex); // Let the state machine sort it out. RequestRun("application failure"); WmSm.UnlockNotif(); }
/// <summary> /// This method can be called by the state machine methods to request /// the state machine to run at Deadline. If Deadline is MinValue, the /// state machine will be run again immediately. /// </summary> public void ScheduleRun(String reason, DateTime deadline) { if (deadline < NextRunDate) { NextRunDate = deadline; WmSm.ScheduleRun(KwmStrings.Kws + m_kws.InternalID + ": " + reason, deadline); } }
/// <summary> /// This method should be called when the UI is exited. /// </summary> public static void OnUiExit() { Debug.Assert(UiEntryCount > 0); UiEntryCount--; if (UiEntryCount == 0) { WmSm.HandleUiExit(); } }
/// <summary> /// Called when the workspace logs out normally. /// </summary> public void HandleNormalLogout() { WmSm.LockNotif(); Debug.Assert(m_ks.LoginStatus == KwsLoginStatus.LoggingOut); // Update our login state. UpdateStateOnLogout(null); WmSm.UnlockNotif(); }
/// <summary> /// Update KcdEventUpToDateFlag, EAnpFreshnessID and handle notifications. /// </summary> private void UpdateKcdEventUpToDateState() { bool newValue = AreKcdEventUpToDate(); if (m_ks.KcdEventUpToDateFlag == newValue) { return; } m_ks.KcdEventUpToDateFlag = newValue; if (newValue) { WmSm.QueueNotif(new KwsSmNotifKcdEventUpToDate(m_kws)); } }
/// <summary> /// Called when the KCD connection status has changed. This method is /// only called by the WM state machine. /// </summary> public void HandleKcdConnStatusChange(KcdConnStatus status, Exception ex) { // Update our login state. if (status == KcdConnStatus.Disconnecting || status == KcdConnStatus.Disconnected) { UpdateStateOnLogout(ex); } // Notify the listeners. WmSm.QueueNotif(new KwsSmNotifKcdConn(m_kws, status, ex)); // Let the state machine sort it out. RequestRun("KCD connection status change"); }
/// <summary> /// Switch the current task to the task specified. Don't call this /// outside RequestSwitchTask() unless you know what you are doing. /// The state machine will run ASAP to handle the new state. /// </summary> private void SwitchTask(KwsTask task, Exception ex) { // The order of the calls is important here. WmSm.LockNotif(); Debug.Assert(m_taskSwitchFlag == false); m_taskSwitchFlag = true; m_cd.CurrentTask = task; StopAppIfNeeded(ex); DisconnectFromKcdIfNeeded(); LogoutIfNeeded(); StopRebuildIfNeeded(); UpdateKcdEventUpToDateState(); m_kws.OnStateChange(WmStateChange.Transient); RequestRun("task switch to " + task); m_taskSwitchFlag = false; WmSm.QueueNotif(new KwsSmNotifTaskSwitch(m_kws, task, ex)); WmSm.UnlockNotif(); }
public static void Relink(WmDeserializer ds) { Cd = ds.WmCd; WmSm.Relink(); KcdBroker.OnEvent += WmSm.HandleKcdBrokerNotification; KmodBroker.OnThreadCollected += WmSm.OnThreadCollected; EAnpBroker.OnClose += WmSm.OnThreadCollected; EAnpBroker.OnChannelOpen += WmEAnp.HandleChannelOpen; foreach (UInt64 internalID in ds.KwsCdList.Keys) { Workspace kws = new Workspace(); KwsCoreData kwsCd = ds.KwsCdList[internalID]; kws.Relink(internalID, kwsCd); } AdjustPublicKwsID(); }
/// <summary> /// Called when the workspace becomes logged in. /// </summary> public void HandleLoginSuccess() { WmSm.LockNotif(); Debug.Assert(m_ks.LoginStatus == KwsLoginStatus.LoggingIn); // We're now logged in. m_ks.LoginStatus = KwsLoginStatus.LoggedIn; // Update the event up to date state. UpdateKcdEventUpToDateState(); // Notify the listeners. WmSm.QueueNotif(new KwsSmNotifKcdLogin(m_kws, m_ks.LoginStatus, null)); // Let the state machine sort it out. RequestRun("Workspace login success"); WmSm.UnlockNotif(); }
/// <summary> /// Dispatch an KCD event and update the state as needed. /// </summary> private void DispatchKcdEvent(AnpMsg msg) { // Dispatch the event to the appropriate handler. KwsAnpEventStatus newStatus = DispatchAnpEventToHandler(msg); // For quenching purposes we assume the event was processed. WmSm.HandleKcdEventProcessed(); // If the ANP event has been processed, update its entry in the // database and the catch up state as required. if (newStatus == KwsAnpEventStatus.Processed) { if (msg.ID > 0) { Debug.Assert(m_ks.NbUnprocessedEvent > 0); m_kws.UpdateKAnpEventStatusInDb(msg.ID, KwsAnpEventStatus.Processed); m_ks.NbUnprocessedEvent--; UpdateKcdEventUpToDateState(); m_kws.OnStateChange(WmStateChange.Permanent); } } }
/// <summary> /// Called when the workspace logs out normally, the login fails or /// the connection to the KCD is lost. /// </summary> private void UpdateStateOnLogout(Exception ex) { if (m_ks.LoginStatus == KwsLoginStatus.LoggedOut) { return; } // Set the last exception. m_kws.Cd.LastException = ex; m_kws.OnStateChange(WmStateChange.Transient); // Update the login status. m_kws.KcdLoginHandler.ResetOnLogout(); // Cancel all the pending KCD queries that depend on the login // state. m_kws.Kcd.CancelKwsKcdQuery(m_kws); // Update the event up to date state. UpdateKcdEventUpToDateState(); // Notify the listeners. WmSm.QueueNotif(new KwsSmNotifKcdLogin(m_kws, m_ks.LoginStatus, ex)); }
/// <summary> /// Request a switch to the task specified, if possible. 'Ex' is /// non-null if the task switch is occurring because an error occurred. /// </summary> public void RequestTaskSwitch(KwsTask task, Exception ex) { WmSm.LockNotif(); try { // Validate. if (m_taskSwitchFlag || task == KwsTask.Stop && !CanStop() || task == KwsTask.Spawn && !CanSpawn() || task == KwsTask.Rebuild && !CanRebuild() || task == KwsTask.WorkOffline && !CanWorkOffline() || task == KwsTask.WorkOnline && !CanWorkOnline() || task == KwsTask.DeleteLocally && !CanDeleteLocally() || task == KwsTask.DeleteRemotely && !CanDeleteRemotely()) { KLogging.Log("Request to switch to task " + task + " ignored."); return; } KLogging.Log("Switching to task " + task + "."); // Update some state prior to the task switch. if (task == KwsTask.Rebuild) { m_cd.MainStatus = KwsMainStatus.RebuildRequired; m_rebuildStep = KwsRebuildTaskStep.None; } else if (task == KwsTask.WorkOnline) { ResetKcdFailureState(); } else if (task == KwsTask.DeleteLocally) { m_cd.MainStatus = KwsMainStatus.OnTheWayOut; m_kws.AddToKwsRemoveTree(); m_kws.OnStateChange(WmStateChange.Permanent); } else if (task == KwsTask.DeleteRemotely) { ResetKcdFailureState(); m_deleteRemotelyStep = KwsDeleteRemotelyStep.ConnectedAndLoggedOut; } if (task == KwsTask.Spawn || task == KwsTask.Rebuild || task == KwsTask.WorkOffline || task == KwsTask.WorkOnline) { m_cd.LastException = null; m_kws.OnStateChange(WmStateChange.Permanent); } // Perform the task switch, if required. if (task != m_cd.CurrentTask) { SwitchTask(task, ex); } } catch (Exception ex2) { KBase.HandleException(ex2, true); } finally { WmSm.UnlockNotif(); } }
/// <summary> /// Request the WM to clear the errors of our KCD, if any. /// </summary> private void ResetKcdFailureState() { WmSm.ResetKcdFailureState(m_kws.Kcd); }
/// <summary> /// Blame the KCD of this workspace for the failure specified. /// </summary> private void BlameKcd(Exception ex) { WmSm.HandleTroublesomeKcd(m_kws.Kcd, ex); }
/// <summary> /// Enter the main mode of the KWM. Return true if the application /// must continue. /// </summary> private static bool EnterMainMode() { // Perform early linking. Wm.LocalDbBroker.Relink(Wm.LocalDb); // FIXME. #if true // Open a temporary console. ConsoleWindow foo = new ConsoleWindow(); foo.Show(); foo.OnConsoleClosing += delegate(Object sender, EventArgs args) { WmSm.RequestStop(); }; // Create an empty database. Wm.LocalDb.DeleteDb(); Wm.LocalDb.OpenOrCreateDb(KwmPath.GetKwmDbPath()); Wm.LocalDbBroker.InitDb(); WmDeserializer ds = new WmDeserializer(); ds.Deserialize(); Debug.Assert(ds.Ex == null); #else // Open or create the database. Wm.LocalDb.OpenOrCreateDb(KwmPath.GetKwmDbPath()); Wm.LocalDbBroker.InitDb(); // Try to deserialize. WmDeserializer ds = new WmDeserializer(); ds.Deserialize(); // The deserialization failed. if (ds.Ex != null) { // If the user doesn't want to recover, bail out. if (!TellUserAboutDsrFailure()) { return(false); } // Backup, delete and recreate a database. BackupDb(); Wm.LocalDb.DeleteDb(); Wm.LocalDb.OpenOrCreateDb(KwmPath.GetKwmDbPath()); Wm.LocalDbBroker.InitDb(); // Retry to deserialize. ds = new WmDeserializer(); ds.Deserialize(); if (ds.Ex != null) { throw ds.Ex; } // Set the next internal workspace ID to a high value based on // the date to avoid conflicts with old KFS directories. ds.WmCd.NextKwsInternalID = (UInt64)(DateTime.Now - new DateTime(2010, 1, 1)).TotalSeconds; } #endif // Relink the workspace manager object graphs. Wm.Relink(ds); // Open the lingering database transaction. Wm.LocalDb.BeginTransaction(); // Serialize the WM state that has changed. Wm.Serialize(); // Export the workspaces, then exit. if (ExportKwsPath != "") { WmKwsImportExport.ExportKws(ExportKwsPath, 0); return(false); } // Set the handle to the message window. SetKwmHandle(MsgWindow.Handle); // Pass the hand to the WM state machine. WmSm.RequestStart(); return(true); }