/// <summary> /// Starts the asynchronous update process. /// </summary> /// <exception cref="InvalidOperationException"><see cref="IsRunning"/> was true.</exception> public void Start() { // Check the current IsRunning state, and set it to true bool isAlreadyRunning; lock (_isRunningSync) { isAlreadyRunning = IsRunning; if (!isAlreadyRunning) { _isRunning = true; } } // Throw an exception if the IsRunning was already true if (isAlreadyRunning) { throw new InvalidOperationException("The UpdateClient is already running."); } try { if (IsRunningChanged != null) { IsRunningChanged(this); } } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } State = UpdateClientState.Initializing; // Create the objects _msr = new MasterServerReader(Settings.LocalFileServerPath, Settings.LocalMasterServerPath); _fileReplacer = Settings.CreateOfflineFileReplacer(); State = UpdateClientState.ReadingLiveVersion; // Start grabbing from the master server _msr.BeginReadVersion(MasterServerReader_Callback, this); }
/// <summary> /// Starts the asynchronous update process. /// </summary> /// <exception cref="InvalidOperationException"><see cref="IsRunning"/> was true.</exception> public void Start() { // Check the current IsRunning state, and set it to true bool isAlreadyRunning; lock (_isRunningSync) { isAlreadyRunning = IsRunning; if (!isAlreadyRunning) _isRunning = true; } // Throw an exception if the IsRunning was already true if (isAlreadyRunning) throw new InvalidOperationException("The UpdateClient is already running."); try { if (IsRunningChanged != null) IsRunningChanged(this); } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } State = UpdateClientState.Initializing; // Create the objects _msr = new MasterServerReader(Settings.LocalFileServerPath, Settings.LocalMasterServerPath); _fileReplacer = Settings.CreateOfflineFileReplacer(); State = UpdateClientState.ReadingLiveVersion; // Start grabbing from the master server _msr.BeginReadVersion(MasterServerReader_Callback, this); }
/// <summary> /// The callback method for the <see cref="IMasterServerReader.BeginReadVersionFileList"/>. /// </summary> /// <param name="sender">The <see cref="IMasterServerReader"/> this event came from.</param> /// <param name="info">The information from the master server(s).</param> /// <param name="userState">An optional state object passed by the caller to supply information to the callback method /// from the method call.</param> void MasterServerReader_Callback_VersionFileList(IMasterServerReader sender, IMasterServerReadInfo info, object userState) { State = UpdateClientState.ReadingLiveVersionFileListDone; // Check for a valid VersionFileList if (string.IsNullOrEmpty(info.VersionFileListText)) { const string errmsg = "Could not get a valid VersionFileList file from the master servers for version `{0}` - download failed."; if (log.IsErrorEnabled) log.ErrorFormat(errmsg, info.Version); if (MasterServerReaderError != null) MasterServerReaderError(this, string.Format(errmsg, info.Version)); HasErrors = true; return; } try { _versionFileList = VersionFileList.CreateFromString(info.VersionFileListText); } catch (Exception ex) { const string errmsg = "Could not get a valid VersionFileList file from the master servers for version `{0}`. Exception: {1}"; if (log.IsErrorEnabled) log.ErrorFormat(errmsg, info.Version, ex); if (MasterServerReaderError != null) MasterServerReaderError(this, string.Format(errmsg, info.Version, ex)); HasErrors = true; return; } // Find the files to update var toUpdate = FindFilesToUpdate(_versionFileList); // If all file hashes match, then we are good to go if (toUpdate.Count() == 0) { CheckIfDownloadManagerComplete(); return; } // There was one or more files to update, so start the updating... // Create the DownloadManager _dm = new DownloadManager(Settings.TargetPath, Settings.TempPath, info.Version); _dm.DownloadFinished += DownloadManager_DownloadFinished; _dm.FileMoveFailed += DownloadManager_FileMoveFailed; _dm.DownloadFailed += DownloadManager_DownloadFailed; _dm.Finished += DownloadManager_Finished; State = UpdateClientState.UpdatingFiles; // Add the sources to the DownloadManager var sources = info.DownloadSources.Select(x => x.Instantiate()); _dm.AddSources(sources); // Enqueue the files that need to be downloaded _dm.Enqueue(toUpdate); }
/// <summary> /// The callback method for the <see cref="IMasterServerReader.BeginReadVersion"/>. /// </summary> /// <param name="sender">The <see cref="IMasterServerReader"/> this event came from.</param> /// <param name="info">The information from the master server(s).</param> /// <param name="userState">An optional state object passed by the caller to supply information to the callback method /// from the method call.</param> void MasterServerReader_Callback(IMasterServerReader sender, IMasterServerReadInfo info, object userState) { State = UpdateClientState.ReadingLiveVersionDone; // Check for errors if (info.Error != null) { HasErrors = true; // Raise error event try { if (MasterServerReaderError != null) MasterServerReaderError(this, info.Error); } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } // Change the state State = UpdateClientState.Completed; // Set to not running lock (_isRunningSync) { Debug.Assert(_isRunning); _isRunning = false; try { if (IsRunningChanged != null) IsRunningChanged(this); } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } } return; } // Set the found live version LiveVersion = info.Version; // Check if the live version equals our version and, if so, there is no need to continue with the update process var currentVersion = Settings.GetCurrentVersion(); if (currentVersion.HasValue && currentVersion.Value == LiveVersion.Value) { // Change the state State = UpdateClientState.Completed; // Set to not running lock (_isRunningSync) { Debug.Assert(_isRunning); _isRunning = false; try { if (IsRunningChanged != null) IsRunningChanged(this); } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } } return; } // Grab the VersionFileList State = UpdateClientState.ReadingLiveVersionFileList; _msr.BeginReadVersionFileList(MasterServerReader_Callback_VersionFileList, info.Version, this); }
/// <summary> /// Runs the clean-up routines. /// </summary> void Cleanup() { State = UpdateClientState.CleaningUp; // Delete files from the update process try { if (_versionFileList != null) { if (Directory.Exists(Settings.TargetPath)) { var ffc = FileFilterHelper.CreateCollection(_versionFileList.Filters); if (ffc.Count > 0) { var pathTrimLen = Settings.TargetPath.Length; if (!Settings.TargetPath.EndsWith(Path.DirectorySeparatorChar.ToString()) && !Settings.TargetPath.EndsWith(Path.AltDirectorySeparatorChar.ToString())) pathTrimLen++; var files = Directory.GetFiles(Settings.TargetPath); foreach (var f in files) { var relativePath = f.Substring(pathTrimLen); try { if (_versionFileList.ContainsFile(relativePath)) continue; if (ffc.IsMatch("\\" + relativePath)) { if (log.IsDebugEnabled) log.DebugFormat( "Skipping deleting outdated client file `{0}` - matches one or more delete skip filters.", f); continue; } if (log.IsInfoEnabled) log.InfoFormat("Deleting outdated client file: {0}", f); File.Delete(f); } catch (Exception ex) { const string errmsg = "Unexpected error while checking if file `{0}` should be deleted. Exception: {1}"; if (log.IsErrorEnabled) log.ErrorFormat(errmsg, f, ex); Debug.Fail(string.Format(errmsg, f, ex)); } } } } } else { const string errmsg = "_versionFileList was null, but wasn't expected to be."; if (log.IsErrorEnabled) log.Error(errmsg); Debug.Fail(errmsg); } } catch (Exception ex) { const string errmsg = "Failed to delete old files. Exception: {0}"; if (log.IsErrorEnabled) log.ErrorFormat(errmsg, ex); Debug.Fail(string.Format(errmsg, ex)); } // Delete temp files try { if (Directory.Exists(Settings.TempPath)) { var tempFiles = Directory.GetFiles(Settings.TempPath); // Delete each file individually first, even though Directory.Delete can do this, just to make sure that // we delete as much as we possibly can if there are errors foreach (var f in tempFiles) { try { if (log.IsDebugEnabled) log.DebugFormat("Deleting temp file `{0}`.", f); File.Delete(f); } catch (Exception ex) { const string errmsg = "Failed to delete temporary file `{0}`. Exception: {1}"; Debug.Fail(string.Format(errmsg, f, ex)); } } // Delete the directory if (log.IsDebugEnabled) log.DebugFormat("Deleting directory `{0}`.", Settings.TempPath); Directory.Delete(Settings.TempPath, true); } } catch (Exception ex) { const string errmsg = "Failed to delete temp files from path `{0}`. Exception: {1}"; if (log.IsErrorEnabled) log.ErrorFormat(errmsg, Settings.TempPath, ex); Debug.Fail(string.Format(errmsg, Settings.TempPath, ex)); } }
/// <summary> /// Checks if the downloading with the <see cref="DownloadManager"/> has been completed. /// </summary> void CheckIfDownloadManagerComplete() { // If the download manager is null, then skip checking it if (_dm != null) { if (!_dm.IsDisposed) { // Do not continue if items are still enqueued for download if (_dm.QueueCount > 0) { const string errmsg = "CheckIfDownloadManagerComplete() called, but items still in the download queue?"; if (log.IsWarnEnabled) log.Warn(errmsg); Debug.Fail(errmsg); return; } // Clean up try { _dm.Dispose(); } catch (Exception ex) { const string errmsg = "Failed to dispose DownloadManager `{0}`. Reason: {1}"; if (log.IsWarnEnabled) log.WarnFormat(errmsg, _dm, ex); Debug.Fail(string.Format(errmsg, _dm, ex)); } } _dm = null; } // Abort if HasErrors is true if (HasErrors) { State = UpdateClientState.Completed; return; } // Clean up Cleanup(); // If done, update the version TrySetClientVersionToLive(); // Change the state State = UpdateClientState.Completed; // Set to not running lock (_isRunningSync) { Debug.Assert(_isRunning); _isRunning = false; try { if (IsRunningChanged != null) IsRunningChanged(this); } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } } }
void _uc_StateChanged(UpdateClient sender, UpdateClientState oldState, UpdateClientState newState) { LogLine("State changed: " + newState); }
/// <summary> /// The callback method for the <see cref="IMasterServerReader.BeginReadVersionFileList"/>. /// </summary> /// <param name="sender">The <see cref="IMasterServerReader"/> this event came from.</param> /// <param name="info">The information from the master server(s).</param> /// <param name="userState">An optional state object passed by the caller to supply information to the callback method /// from the method call.</param> void MasterServerReader_Callback_VersionFileList(IMasterServerReader sender, IMasterServerReadInfo info, object userState) { State = UpdateClientState.ReadingLiveVersionFileListDone; // Check for a valid VersionFileList if (string.IsNullOrEmpty(info.VersionFileListText)) { const string errmsg = "Could not get a valid VersionFileList file from the master servers for version `{0}` - download failed."; if (log.IsErrorEnabled) { log.ErrorFormat(errmsg, info.Version); } if (MasterServerReaderError != null) { MasterServerReaderError(this, string.Format(errmsg, info.Version)); } HasErrors = true; return; } try { _versionFileList = VersionFileList.CreateFromString(info.VersionFileListText); } catch (Exception ex) { const string errmsg = "Could not get a valid VersionFileList file from the master servers for version `{0}`. Exception: {1}"; if (log.IsErrorEnabled) { log.ErrorFormat(errmsg, info.Version, ex); } if (MasterServerReaderError != null) { MasterServerReaderError(this, string.Format(errmsg, info.Version, ex)); } HasErrors = true; return; } // Find the files to update var toUpdate = FindFilesToUpdate(_versionFileList); // If all file hashes match, then we are good to go if (toUpdate.Count() == 0) { CheckIfDownloadManagerComplete(); return; } // There was one or more files to update, so start the updating... // Create the DownloadManager _dm = new DownloadManager(Settings.TargetPath, Settings.TempPath, info.Version); _dm.DownloadFinished += DownloadManager_DownloadFinished; _dm.FileMoveFailed += DownloadManager_FileMoveFailed; _dm.DownloadFailed += DownloadManager_DownloadFailed; _dm.Finished += DownloadManager_Finished; State = UpdateClientState.UpdatingFiles; // Add the sources to the DownloadManager var sources = info.DownloadSources.Select(x => x.Instantiate()); _dm.AddSources(sources); // Enqueue the files that need to be downloaded _dm.Enqueue(toUpdate); }
/// <summary> /// The callback method for the <see cref="IMasterServerReader.BeginReadVersion"/>. /// </summary> /// <param name="sender">The <see cref="IMasterServerReader"/> this event came from.</param> /// <param name="info">The information from the master server(s).</param> /// <param name="userState">An optional state object passed by the caller to supply information to the callback method /// from the method call.</param> void MasterServerReader_Callback(IMasterServerReader sender, IMasterServerReadInfo info, object userState) { State = UpdateClientState.ReadingLiveVersionDone; // Check for errors if (info.Error != null) { HasErrors = true; // Raise error event try { if (MasterServerReaderError != null) { MasterServerReaderError(this, info.Error); } } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } // Change the state State = UpdateClientState.Completed; // Set to not running lock (_isRunningSync) { Debug.Assert(_isRunning); _isRunning = false; try { if (IsRunningChanged != null) { IsRunningChanged(this); } } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } } return; } // Set the found live version LiveVersion = info.Version; // Check if the live version equals our version and, if so, there is no need to continue with the update process var currentVersion = Settings.GetCurrentVersion(); if (currentVersion.HasValue && currentVersion.Value == LiveVersion.Value) { // Change the state State = UpdateClientState.Completed; // Set to not running lock (_isRunningSync) { Debug.Assert(_isRunning); _isRunning = false; try { if (IsRunningChanged != null) { IsRunningChanged(this); } } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } } return; } // Grab the VersionFileList State = UpdateClientState.ReadingLiveVersionFileList; _msr.BeginReadVersionFileList(MasterServerReader_Callback_VersionFileList, info.Version, this); }
/// <summary> /// Runs the clean-up routines. /// </summary> void Cleanup() { State = UpdateClientState.CleaningUp; // Delete files from the update process try { if (_versionFileList != null) { if (Directory.Exists(Settings.TargetPath)) { var ffc = FileFilterHelper.CreateCollection(_versionFileList.Filters); if (ffc.Count > 0) { var pathTrimLen = Settings.TargetPath.Length; if (!Settings.TargetPath.EndsWith(Path.DirectorySeparatorChar.ToString()) && !Settings.TargetPath.EndsWith(Path.AltDirectorySeparatorChar.ToString())) { pathTrimLen++; } var files = Directory.GetFiles(Settings.TargetPath); foreach (var f in files) { var relativePath = f.Substring(pathTrimLen); try { if (_versionFileList.ContainsFile(relativePath)) { continue; } if (ffc.IsMatch("\\" + relativePath)) { if (log.IsDebugEnabled) { log.DebugFormat( "Skipping deleting outdated client file `{0}` - matches one or more delete skip filters.", f); } continue; } if (log.IsInfoEnabled) { log.InfoFormat("Deleting outdated client file: {0}", f); } File.Delete(f); } catch (Exception ex) { const string errmsg = "Unexpected error while checking if file `{0}` should be deleted. Exception: {1}"; if (log.IsErrorEnabled) { log.ErrorFormat(errmsg, f, ex); } Debug.Fail(string.Format(errmsg, f, ex)); } } } } } else { const string errmsg = "_versionFileList was null, but wasn't expected to be."; if (log.IsErrorEnabled) { log.Error(errmsg); } Debug.Fail(errmsg); } } catch (Exception ex) { const string errmsg = "Failed to delete old files. Exception: {0}"; if (log.IsErrorEnabled) { log.ErrorFormat(errmsg, ex); } Debug.Fail(string.Format(errmsg, ex)); } // Delete temp files try { if (Directory.Exists(Settings.TempPath)) { var tempFiles = Directory.GetFiles(Settings.TempPath); // Delete each file individually first, even though Directory.Delete can do this, just to make sure that // we delete as much as we possibly can if there are errors foreach (var f in tempFiles) { try { if (log.IsDebugEnabled) { log.DebugFormat("Deleting temp file `{0}`.", f); } File.Delete(f); } catch (Exception ex) { const string errmsg = "Failed to delete temporary file `{0}`. Exception: {1}"; Debug.Fail(string.Format(errmsg, f, ex)); } } // Delete the directory if (log.IsDebugEnabled) { log.DebugFormat("Deleting directory `{0}`.", Settings.TempPath); } Directory.Delete(Settings.TempPath, true); } } catch (Exception ex) { const string errmsg = "Failed to delete temp files from path `{0}`. Exception: {1}"; if (log.IsErrorEnabled) { log.ErrorFormat(errmsg, Settings.TempPath, ex); } Debug.Fail(string.Format(errmsg, Settings.TempPath, ex)); } }
/// <summary> /// Checks if the downloading with the <see cref="DownloadManager"/> has been completed. /// </summary> void CheckIfDownloadManagerComplete() { // If the download manager is null, then skip checking it if (_dm != null) { if (!_dm.IsDisposed) { // Do not continue if items are still enqueued for download if (_dm.QueueCount > 0) { const string errmsg = "CheckIfDownloadManagerComplete() called, but items still in the download queue?"; if (log.IsWarnEnabled) { log.Warn(errmsg); } Debug.Fail(errmsg); return; } // Clean up try { _dm.Dispose(); } catch (Exception ex) { const string errmsg = "Failed to dispose DownloadManager `{0}`. Reason: {1}"; if (log.IsWarnEnabled) { log.WarnFormat(errmsg, _dm, ex); } Debug.Fail(string.Format(errmsg, _dm, ex)); } } _dm = null; } // Abort if HasErrors is true if (HasErrors) { State = UpdateClientState.Completed; return; } // Clean up Cleanup(); // If done, update the version TrySetClientVersionToLive(); // Change the state State = UpdateClientState.Completed; // Set to not running lock (_isRunningSync) { Debug.Assert(_isRunning); _isRunning = false; try { if (IsRunningChanged != null) { IsRunningChanged(this); } } catch (NullReferenceException ex) { Debug.Fail(ex.ToString()); } } }