/// <summary> /// Creates a <see cref="WuApiController"/> which uses the given Interfaces to search, download and install updates. /// </summary> /// <param name="session">Session to be used.</param> /// <param name="updateCollectionFactory">Factory to create <see cref="IUpdateCollection"/>s.</param> /// <param name="systemInfo">System informations about the OS enviroment.</param> public WuApiController(IUpdateSession session, UpdateCollectionFactory updateCollectionFactory, ISystemInfo systemInfo) { if (session == null) { throw new ArgumentNullException(nameof(session)); } if (updateCollectionFactory == null) { throw new ArgumentNullException(nameof(updateCollectionFactory)); } if (systemInfo == null) { throw new ArgumentNullException(nameof(systemInfo)); } UpdateSession = session; UpdateSession.ClientApplicationID = this.GetType().FullName; UpdateSearcher = session.CreateUpdateSearcher(); UpdateDownloader = session.CreateUpdateDownloader(); UpdateInstaller = session.CreateUpdateInstaller(); UpdateCollectionFactory = updateCollectionFactory; SystemInfo = systemInfo; StateTransitions = SetupStateTransitions(); EnterState((SystemInfo.IsRebootRequired()) ? (WuProcessState) new WuStateRebootRequired() : new WuStateReady()); Log.Info("Initial state: " + _currentState.GetType().Name); if (Log.IsDebugEnabled) { OnStateChanged += (s, e) => { Log.Debug($"Event {nameof(OnStateChanged)} fired: {e.ToString()}"); }; OnAsyncOperationCompleted += (s, e) => { Log.Debug($"Event {nameof(OnAsyncOperationCompleted)} fired: {e.ToString()}"); }; OnProgressChanged += (s, e) => { Log.Debug($"Event {nameof(OnProgressChanged)} fired: {e.ToString()}"); }; } }
/// <summary> /// Leaves and disposes the current state and enters a new state. /// </summary> /// <param name="next">The new state to change to.</param> private void EnterState(WuProcessState next) { if (next == null) { throw new ArgumentNullException(nameof(next)); } using (ll.Lock(StateChangingLock)) { if (_currentState != null) { ThrowIfInvalidStateTransition(next.GetType()); } Log.Info($"Changing state from '{_currentState?.DisplayName}' to '{next.DisplayName}'."); WuProcessState oldState = _currentState; try { next.EnterState(oldState); } catch (Exception e) { Log.Error($"Failed to enter state '{next?.DisplayName}' properly.", e); Debug.Assert(_currentState != next, "Should not switch to the next state when an error occured."); throw; } _currentState = next; CurrentProgress = null; try { oldState?.LeaveState(); } catch (Exception e) { Debug.Assert(true, $"Failed to leave state '{oldState?.DisplayName}' properly: {e.Message}."); // do not hide this exception in test scenarios Log.Error($"Failed to leave state '{oldState?.DisplayName}' properly.", e); } finally { (oldState as IDisposable)?.Dispose(); } OnStateChanged?.Invoke(this, new StateChangedEventArgs((oldState != null) ? oldState.StateId : WuStateId.Ready, next.StateId)); if (OnAsyncOperationCompleted != null && oldState is WuStateAsyncJob) { if (oldState is WuStateSearching) { OnAsyncOperationCompleted(this, new AsyncOperationCompletedEventArgs(AsyncOperation.Searching, next.StateId)); } else if (oldState is WuStateDownloading) { OnAsyncOperationCompleted(this, new AsyncOperationCompletedEventArgs(AsyncOperation.Downloading, next.StateId)); } else if (oldState is WuStateInstalling) { OnAsyncOperationCompleted(this, new AsyncOperationCompletedEventArgs(AsyncOperation.Installing, next.StateId)); } else { throw new NotImplementedException($"For {oldState.GetType()} are no {nameof(OnAsyncOperationCompleted)} event args implemented."); } } Debug.Assert(_currentState == next, "State was not changed."); Debug.Assert(CurrentProgress == null, "Should reset progress when state changes."); } }