// // Copyright (c) 2012 Microsoft Corporation. All rights reserved. // // DISCLAIMER OF WARRANTY: The software is licensed “as-is.” You // bear the risk of using it. Microsoft gives no express warranties, // guarantees or conditions. You may have additional consumer rights // under your local laws which this agreement cannot change. To the extent // permitted under your local laws, Microsoft excludes the implied warranties // of merchantability, fitness for a particular purpose and non-infringement. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.IO; using System.Threading; namespace JobSourceAdapterSample { /// /// Simple cmdlet to create a FileCopyJob. /// [Cmdlet(VerbsCommon.Get, "FileCopyJob")] [OutputType(typeof(Job2))] public sealed class StartFileCopyJobCommand : PSCmdlet { #region Parameters [Parameter(Position = 0, Mandatory = true)] [ValidateNotNullOrEmpty] public string Name { get; set; } [Parameter(Position = 1, Mandatory = true)] [ValidateNotNullOrEmpty] public string SourcePath { get; set; } [Parameter(Position = 2, Mandatory = true)] [ValidateNotNullOrEmpty] public string DestinationPath { get; set; } #endregion #region Overrides protected override void ProcessRecord() { // Get full path to source. ProviderInfo provider; string fullSourcePath = GetResolvedProviderPathFromPSPath(SourcePath, out provider).FirstOrDefault(); if (string.IsNullOrEmpty(fullSourcePath)) { throw new ArgumentException(SourcePath); } // Get full path for destination file (which may not exist). string fullDestPath = null; string destFile = Path.GetFileName(DestinationPath); string path = GetResolvedProviderPathFromPSPath( Path.GetDirectoryName(DestinationPath), out provider).FirstOrDefault(); if (path != null) { fullDestPath = Path.Combine(path, destFile); } if (string.IsNullOrEmpty(fullDestPath)) { throw new ArgumentException(DestinationPath); } // Create job source adapter. FileCopyJobSourceAdapter jobSourceAdapter = new FileCopyJobSourceAdapter(); // Create FileCopyJob parameters (source and destination paths). Dictionary copyJobParameters = new Dictionary(); copyJobParameters.Add(FileCopyJobSourceAdapter.SourcePathProperty, fullSourcePath); copyJobParameters.Add(FileCopyJobSourceAdapter.DestinationPathProperty, fullDestPath); // Create job specification. JobInvocationInfo copyJobSpecification = new JobInvocationInfo( new JobDefinition(typeof(FileCopyJobSourceAdapter), string.Empty, Name), copyJobParameters); copyJobSpecification.Name = Name; // Create file copy job from job source adapter and start it. Job2 fileCopyJob = jobSourceAdapter.NewJob(copyJobSpecification); fileCopyJob.StartJob(); WriteObject(fileCopyJob); } #endregion } /// /// Sample FileCopyJob class derived from Job2. /// This sample job runs until stopped and monitors the provided /// source file for changes and copies it to the destination path /// on change. /// public sealed class FileCopyJob : Job2 { #region Private members private const string FileCopyJobTypeName = "FileCopyJob"; private FileSystemWatcher _fileWatcher; private string _sourcePath; private string _destinationPath; #endregion #region Constructor private FileCopyJob() { } /// /// Constructor /// /// Job name /// Source file path /// Copy to destination file path public FileCopyJob( string jobName, string sourcePath, string destinationPath) { if (string.IsNullOrEmpty(sourcePath)) { throw new ArgumentException("sourcePath"); } if (string.IsNullOrEmpty(destinationPath)) { throw new ArgumentException("destPath"); } _sourcePath = sourcePath; _destinationPath = destinationPath; this.Name = !string.IsNullOrEmpty(jobName) ? jobName : "FileCopyJob"; this.PSJobTypeName = FileCopyJobTypeName; // File watcher. _fileWatcher = new FileSystemWatcher(Path.GetDirectoryName(_sourcePath)); _fileWatcher.Filter = Path.GetFileName(_sourcePath); _fileWatcher.EnableRaisingEvents = false; _fileWatcher.NotifyFilter = NotifyFilters.LastWrite; _fileWatcher.Changed += new FileSystemEventHandler(HandleFileWatcherChanged); // Job state changed callback. this.StateChanged += new EventHandler(HandleJobStateChanged); } #endregion #region Public properties public string SourcePath { get { return _sourcePath; } } public string DestinationPath { get { return _destinationPath; } } #endregion #region Public methods public override void StartJob() { if (this.JobStateInfo.State != JobState.NotStarted) { throw new InvalidOperationException("Cannot start job."); } SetJobState(JobState.Running); _fileWatcher.EnableRaisingEvents = true; } public override void StartJobAsync() { StartJob(); OnStartJobCompleted(new System.ComponentModel.AsyncCompletedEventArgs(null, false, null)); } public override void StopJob() { if (!IsFinishedState(this.JobStateInfo.State)) { SetJobState(JobState.Stopped); } } public override void StopJobAsync() { StopJob(); OnStopJobCompleted(new System.ComponentModel.AsyncCompletedEventArgs(null, false, null)); } public override void StopJob(bool force, string reason) { StopJob(); } public override void StopJobAsync(bool force, string reason) { StopJobAsync(); } public override void SuspendJob() { switch (this.JobStateInfo.State) { case JobState.Suspended: break; case JobState.Running: _fileWatcher.EnableRaisingEvents = false; SetJobState(JobState.Suspended); break; default: throw new InvalidOperationException("Cannot suspend job."); } } public override void SuspendJobAsync() { SuspendJob(); OnSuspendJobCompleted(new System.ComponentModel.AsyncCompletedEventArgs(null, false, null)); } public override void SuspendJob(bool force, string reason) { SuspendJob(); } public override void SuspendJobAsync(bool force, string reason) { SuspendJobAsync(); } public override void ResumeJob() { switch (this.JobStateInfo.State) { case JobState.Running: break; case JobState.Suspended: SetJobState(JobState.Running); _fileWatcher.EnableRaisingEvents = true; break; default: throw new InvalidOperationException("Cannot resume job."); } } public override void ResumeJobAsync() { ResumeJob(); OnResumeJobCompleted(new System.ComponentModel.AsyncCompletedEventArgs(null, false, null)); } public override void UnblockJob() { throw new NotImplementedException(); } public override void UnblockJobAsync() { throw new NotImplementedException(); } public override bool HasMoreData { get { return (Output.Count > 0 || Error.Count > 0); } } public override string Location { get { return "localhost"; } } public override string StatusMessage { get { return string.Empty; } } #endregion #region IDispose protected override void Dispose(bool disposing) { if (!IsFinishedState(this.JobStateInfo.State)) { SetJobState(JobState.Stopped); } base.Dispose(disposing); } #endregion #region Private methods private void HandleFileWatcherChanged(object sender, FileSystemEventArgs e) { if (IsFinishedState(this.JobStateInfo.State)) { return; } Exception failed = null; try { File.Copy(_sourcePath, _destinationPath, true); } catch (IOException ex) { failed = ex; } catch (UnauthorizedAccessException ex) { failed = ex; } if (failed != null && Error.IsOpen) { try { Error.Add(new ErrorRecord(failed, "FileCopyFailed", ErrorCategory.WriteError, this)); } catch (PSInvalidOperationException) { // Can't write to closed buffer. } } } private void HandleJobStateChanged(object sender, JobStateEventArgs e) { if (IsFinishedState(e.JobStateInfo.State)) { // Job transitioned to finished state. this.StateChanged -= new EventHandler(HandleJobStateChanged); DisposeFileWatcher(); } } private void DisposeFileWatcher() { if (_fileWatcher != null) { _fileWatcher.Changed -= new FileSystemEventHandler(HandleFileWatcherChanged); _fileWatcher.Dispose(); _fileWatcher = null; } } private bool IsFinishedState(JobState state) { return (state == JobState.Completed || state == JobState.Stopped || state == JobState.Failed); } #endregion } /// /// Sample JobSourceAdapter for a file copy job. /// Creates new CopyFileJob jobs. /// Maintains repository for CopyFileJobs. /// public sealed class FileCopyJobSourceAdapter : JobSourceAdapter { #region Private members private const string AdapterTypeName = "FileCopyJobSourceAdapter"; private static List JobRepository = new List(); #endregion #region Public strings // FileCopy job properties. public const string SourcePathProperty = "SourcePath"; public const string DestinationPathProperty = "DestinationPath"; #endregion #region Constructor public FileCopyJobSourceAdapter() { this.Name = AdapterTypeName; } #endregion #region Public methods public override Job2 NewJob(JobInvocationInfo specification) { if (specification == null) { throw new NullReferenceException("specification"); } if (specification.Parameters.Count != 1) { throw new ArgumentException("JobInvocationInfo specification parameters not specified."); } // Retrieve source and destination path information from specification // parameters. string sourcePath = null; string destinationPath = null; CommandParameterCollection parameters = specification.Parameters[0]; foreach (var item in parameters) { if (item.Name.Equals(SourcePathProperty, StringComparison.OrdinalIgnoreCase)) { sourcePath = item.Value as string; } else if (item.Name.Equals(DestinationPathProperty, StringComparison.OrdinalIgnoreCase)) { destinationPath = item.Value as string; } } // Create FileCopyJob FileCopyJob rtnJob = new FileCopyJob(specification.Name, sourcePath, destinationPath); lock (JobRepository) { JobRepository.Add(rtnJob); } return rtnJob; } public override void RemoveJob(Job2 job) { lock (JobRepository) { if (JobRepository.Contains(job)) { JobRepository.Remove(job); } } job.Dispose(); } public override IList GetJobs() { lock (JobRepository) { return JobRepository.ToArray(); } } public override Job2 GetJobByInstanceId(Guid instanceId, bool recurse) { lock (JobRepository) { foreach (var job in JobRepository) { if (job.InstanceId == instanceId) { return job; } } } return null; } public override Job2 GetJobBySessionId(int id, bool recurse) { lock (JobRepository) { foreach (var job in JobRepository) { if (job.Id == id) { return job; } } } return null; } public override IList GetJobsByName(string name, bool recurse) { List rtnJobs = new List(); WildcardPattern namePattern = new WildcardPattern(name, WildcardOptions.IgnoreCase); lock (JobRepository) { foreach (var job in JobRepository) { if (namePattern.IsMatch(job.Name)) { rtnJobs.Add(job); } } } return rtnJobs; } public override IList GetJobsByState(JobState state, bool recurse) { List rtnJobs = new List(); lock (JobRepository) { foreach (var job in JobRepository) { if (job.JobStateInfo.State == state) { rtnJobs.Add(job); } } } return rtnJobs; } public override IList GetJobsByCommand(string command, bool recurse) { throw new NotImplementedException(); } public override IList GetJobsByFilter(Dictionary filter, bool recurse) { throw new NotImplementedException(); } #endregion } }