private static void StartTasksAndWaitForAll(IEnumerable <Task> tasks, ISyncJobExecutionContext ctx) { var runningTasks = new List <Task>(); foreach (var t in tasks) { try { if (ctx.CancelToken.IsCancellationRequested) { break; } t.Start(); runningTasks.Add(t); } catch { //TODO log } } TaskHelper.WaitAll(runningTasks); }
// Private Methods (20) private static void CompareDirectoriesSyncAction(DirectoryInfo src, DirectoryInfo dest, ISyncJobExecutionContext execCtx) { CreateCompareDirectoriesTask(src: src, dest: dest, execCtx: execCtx, recursive: true).RunSynchronously(); }
private static Task CreateCopyFileTask(FileInfo src, FileInfo dest, ISyncJobExecutionContext execCtx) { return(new Task((state) => { const string LOG_CATEGORY = "CopyFile"; var ctx = (ISyncJobExecutionContext)state; try { src.Refresh(); if (!src.Exists) { return; } var destDir = dest.Directory; destDir.Refresh(); if (!destDir.Exists) { return; } dest.Refresh(); File.Copy(src.FullName, dest.FullName, true); ctx.Log(msg: string.Format("File '{0}' was copied to '{1}'.", src.FullName, dest.Directory.FullName), tag: LOG_CATEGORY, type: SyncLogType.OK); } catch (Exception ex) { ctx.Log(msg: string.Format("Copying file '{0}' to '{1}' failed: {2}", src.FullName, dest.Directory.FullName, ex.GetBaseException() ?? ex), tag: LOG_CATEGORY, type: SyncLogType.Error); } finally { TrySyncCreationTimes(src, dest); TrySyncLastWriteTimes(src, dest); } }, state: execCtx , cancellationToken: execCtx.CancelToken)); }
// Public Methods (1) /// <summary> /// /// </summary> /// <see cref="ISyncJobAction.Execute(ISyncJobExecutionContext)" /> public void Execute(ISyncJobExecutionContext ctx) { lock (this._SYNC) { if (ctx == null) { throw new ArgumentNullException("ctx"); } try { this.OnExecute(ctx); } catch (Exception ex) { throw ex as AggregateException ?? new AggregateException(ex); } } }
private FileSystemEventHandler CreateFileSystemEventHandlerForSource(ISyncJobExecutionContext ctx) { return(new FileSystemEventHandler((sender, e) => { lock (ctx.SyncRoot) { try { var watchedDir = Path.GetDirectoryName(e.FullPath); var normalizedWatchedDir = ToComparablePath(watchedDir); var action = ctx.Queue .OrderBy(a => ToComparablePath(a.Tag.AsString(true)), StringComparer.InvariantCultureIgnoreCase) .FirstOrDefault(a => normalizedWatchedDir.Equals(a.Tag)); if (action != null) { return; } var relativeSrcPath = GetRelativePath(ctx.SourceDirectory, watchedDir); action = new DelegateSyncAction(action: CompareDirectoriesSyncAction, src: new DirectoryInfo(watchedDir), dest: new DirectoryInfo(Path.Combine(ctx.DestionationDirectory, relativeSrcPath))); action.Tag = normalizedWatchedDir; ctx.Queue .Enqueue(action); } catch (Exception ex) { ctx.Log(msg: string.Format("Sync failed: {0}", ex.GetBaseException() ?? ex), tag: "FileSystemEvent", type: SyncLogType.Error); } } })); }
private void HandleSyncActions(FileSystemWatcher watcher, ISyncJobExecutionContext ctx) { while (!ctx.CancelSource.IsCancellationRequested) { try { ISyncJobAction action; if (ctx.Queue.TryDequeue(out action)) { action.Execute(ctx); } } catch (Exception ex) { ctx.Log(msg: string.Format("Handling of sync action failed: {0}", ex.GetBaseException() ?? ex), tag: "HandleSyncAction", type: SyncLogType.Error); } } }
private static Task CreateDeleteFileTask(FileInfo file, ISyncJobExecutionContext execCtx) { return(new Task((state) => { const string LOG_CATEGORY = "CopyFile"; var ctx = (ISyncJobExecutionContext)state; if (ctx.CancelToken.IsCancellationRequested) { return; } try { file.Refresh(); if (file.Exists) { file.Delete(); file.Refresh(); ctx.Log(msg: string.Format("File '{0}' was deleted.", file.FullName), tag: LOG_CATEGORY, type: SyncLogType.OK); } } catch (Exception ex) { ctx.Log(msg: string.Format("Deleting file '{0}' failed: {1}", file.FullName, ex.GetBaseException() ?? ex), tag: LOG_CATEGORY, type: SyncLogType.Error); } }, state: execCtx , cancellationToken: execCtx.CancelToken)); }
// Protected Methods (1) protected override void OnExecute(ISyncJobExecutionContext ctx) { this._ACTION(this._SOURCE, this._DESTINATION, ctx); }
private static Task CreateDelTreeTask(DirectoryInfo dir, ISyncJobExecutionContext execCtx) { return(new Task((state) => { const string LOG_CATEGORY = "DelTree"; var ctx = (ISyncJobExecutionContext)state; try { dir.Refresh(); // files StartTasksAndWaitForAll(dir.GetFiles() .Select(f => CreateDeleteFileTask(f, ctx)), ctx); // directories StartTasksAndWaitForAll(dir.GetDirectories() .Select(d => CreateDelTreeTask(d, ctx)), ctx); if (!ctx.CancelToken.IsCancellationRequested) { dir.Refresh(); if (NormalizePath(dir) == NormalizePath(ctx.SourceDirectory) || NormalizePath(dir) == NormalizePath(ctx.DestionationDirectory)) { // do not delete source or destination directory return; } if (dir.GetFiles().IsEmpty() && dir.GetDirectories().IsEmpty()) { if (dir.Exists) { dir.Delete(); dir.Refresh(); ctx.Log(msg: string.Format("Directory '{0}' was deleted.", dir.FullName), tag: LOG_CATEGORY, type: SyncLogType.OK); } } else { ctx.Log(msg: string.Format("Directory '{0}' is NOT empty!", dir.FullName), tag: LOG_CATEGORY, type: SyncLogType.Warning); } } } catch (Exception ex) { ctx.Log(msg: string.Format("Deleting directory structure '{0}' failed: {1}", dir.FullName, ex.GetBaseException() ?? ex), tag: LOG_CATEGORY, type: SyncLogType.Error); } }, state: execCtx , cancellationToken: execCtx.CancelToken)); }
private static Task CreateCompareDirectoriesTask(DirectoryInfo src, DirectoryInfo dest, ISyncJobExecutionContext execCtx, bool recursive) { return(new Task((state) => { const string LOG_CATEGORY = "CompareDirectories"; var ctx = (ISyncJobExecutionContext)state; try { if (!NormalizePath(src).StartsWith(NormalizePath(ctx.SourceDirectory))) { // must be source or inside source directory return; } if (!NormalizePath(dest).StartsWith(NormalizePath(ctx.DestionationDirectory))) { // must be destination or inside destination directory return; } src.Refresh(); if (!src.Exists) { return; } var srcDisplayText = GetRelativePath(ctx.SourceDirectory, src.FullName); if (string.IsNullOrWhiteSpace(srcDisplayText)) { srcDisplayText = src.FullName; } var destDisplayText = GetRelativePath(ctx.DestionationDirectory, dest.FullName); if (string.IsNullOrWhiteSpace(destDisplayText)) { destDisplayText = dest.FullName; } ctx.RaiseProgressChanged(text: string.Format("Comparing '{0}' with '{1}'...", srcDisplayText, destDisplayText)); // EXTRA items { // files if (!ctx.CancelToken.IsCancellationRequested) { dest.Refresh(); if (dest.Exists) { var tasks = new List <Task>(); foreach (var destFile in dest.GetFiles()) { try { var srcFile = new FileInfo(Path.Combine(src.FullName, destFile.Name)); if (!srcFile.Exists) { tasks.Add(CreateDeleteFileTask(destFile, ctx)); } } catch { //TODO log } } StartTasksAndWaitForAll(tasks, ctx); } } // directories if (!ctx.CancelToken.IsCancellationRequested) { dest.Refresh(); if (dest.Exists) { var tasks = new List <Task>(); foreach (var destSubDir in dest.GetDirectories()) { try { var srcSubDir = new DirectoryInfo(Path.Combine(src.FullName, destSubDir.Name)); if (!srcSubDir.Exists) { tasks.Add(CreateDelTreeTask(destSubDir, ctx)); } } catch { //TODO log } } StartTasksAndWaitForAll(tasks, ctx); } } } // copy items { // files if (!ctx.CancelToken.IsCancellationRequested) { dest.Refresh(); var tasks = new List <Task>(); // copy files foreach (var srcFile in src.GetFiles()) { try { var destFile = new FileInfo(Path.Combine(dest.FullName, srcFile.Name)); var copyFile = false; if (destFile.Exists) { copyFile = (srcFile.Length != destFile.Length) || (srcFile.LastWriteTimeUtc != destFile.LastWriteTimeUtc); } else { copyFile = true; } if (copyFile) { destFile.Directory .CreateDirectoryDeep(refreshBefore: true, refreshAfter: true); tasks.Add(CreateCopyFileTask(srcFile, destFile, ctx)); } } catch { //TODO log } } StartTasksAndWaitForAll(tasks, ctx); } // sub directories if (!ctx.CancelToken.IsCancellationRequested) { var tasks = new List <Task>(); foreach (var srcSubDir in src.GetDirectories()) { try { var destSubDir = new DirectoryInfo(Path.Combine(dest.FullName, srcSubDir.Name)); try { if (!destSubDir.Exists) { destSubDir.CreateDirectoryDeep(refreshBefore: false, refreshAfter: true); ctx.Log(msg: string.Format("Destionation directory '{0}' was created.", destSubDir.FullName), tag: LOG_CATEGORY, type: SyncLogType.OK); } } finally { tasks.Add(CreateCompareDirectoriesTask(srcSubDir, destSubDir, ctx, recursive: true)); } } catch { //TODO log } } StartTasksAndWaitForAll(tasks, ctx); } } ctx.RaiseProgressChanged(text: "Done"); } catch (Exception ex) { ctx.Log(msg: string.Format("Comparing directory '{0}' to '{1}' failed: {2}", src.FullName, dest.FullName, ex.GetBaseException() ?? ex), tag: LOG_CATEGORY, type: SyncLogType.Error); } finally { TrySyncCreationTimes(src, dest); TrySyncLastWriteTimes(src, dest); } }, state: execCtx , cancellationToken: execCtx.CancelToken)); }
// Protected Methods (1) /// <summary> /// The logic for <see cref="SyncJobActionBase.Execute()" /> method. /// </summary> /// <param name="ctx">The underlying context.</param> protected abstract void OnExecute(ISyncJobExecutionContext ctx);