From 6c2fe65dbb9117c747a77c5a3a926460450c8573 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 15 Aug 2017 16:21:32 -0700 Subject: [PATCH] Remove src\Microsoft.PowerShell.Workflow.ServiceCore --- .../AssemblyInfo.cs | 27 - .../WorkflowCore/ActivityCategories.cs | 99 - .../WorkflowCore/ActivityHostManager.cs | 673 -- .../WorkflowCore/ActivityHostProcess.cs | 564 -- .../ServiceCore/WorkflowCore/ActivityUtils.cs | 116 - .../WorkflowCore/ConnectionManager.cs | 2372 ------ .../ServiceCore/WorkflowCore/Constants.cs | 91 - .../DefaultWorkflowSessionConfiguration.cs | 126 - .../WorkflowCore/DefinitionCache.cs | 608 -- .../FileInstanceStore/AsyncResult.cs | 174 - .../FileInstanceStore/FileInstanceStore.cs | 225 - .../InstanceStoreCryptography.cs | 68 - .../InstanceStorePermission.cs | 44 - .../FileInstanceStore/TypedAsyncResult.cs | 41 - .../TypedCompletedAsyncResult.cs | 35 - .../WorkflowAdditionalStores.cs | 2039 ----- .../WorkflowCore/GlobalConfiguration.cs | 930 --- .../WorkflowCore/ImportWorkflowCommand.cs | 1874 ----- .../WorkflowCore/LocalRunspaceProvider.cs | 407 - .../NewPSWorkflowExecutionOptionCommand.cs | 876 --- .../WorkflowCore/PSActivityBase.cs | 6577 ----------------- .../WorkflowCore/PSActivityHostManager.cs | 189 - .../WorkflowCore/PSCleanupActivity.cs | 119 - .../WorkflowCore/PSRemotingActivitiesBase.cs | 257 - .../PSSelfRemotingActivitiesBase.cs | 854 --- .../WorkflowCore/PSWorkflowHost.cs | 243 - .../WorkflowCore/PowerShellWorkflowHost.cs | 295 - .../ServiceCore/WorkflowCore/RuntimeBase.cs | 834 --- .../WorkflowCore/TimeBasedCache.cs | 217 - .../WorkflowCore/WorkflowDebugger.cs | 1592 ---- .../WorkflowCore/WorkflowDefinition.cs | 91 - .../WorkflowCore/WorkflowInstance.cs | 2613 ------- .../ServiceCore/WorkflowCore/WorkflowJob2.cs | 2205 ------ .../WorkflowCore/WorkflowJobSourceAdapter.cs | 1019 --- .../WorkflowCore/WorkflowManager.cs | 1482 ---- .../WorkflowCore/WorkflowMetadatas.cs | 105 - .../WorkflowPerformanceCounterSetInfo.cs | 107 - .../WorkflowRuntimeCompilation.cs | 265 - .../WorkflowCore/WorkflowStatusEventArgs.cs | 126 - .../WorkflowCore/WorkflowTimers.cs | 484 -- .../WorkflowTrackingParticipant.cs | 204 - .../ServiceCore/WorkflowCore/WorkflowUtils.cs | 323 - .../WorkflowCore/WorkflowValidation.cs | 401 - .../resources/Resources.resx | 429 -- 44 files changed, 32420 deletions(-) delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/AssemblyInfo.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityCategories.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostManager.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostProcess.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityUtils.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ConnectionManager.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/Constants.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefaultWorkflowSessionConfiguration.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefinitionCache.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/AsyncResult.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/FileInstanceStore.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStoreCryptography.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStorePermission.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedAsyncResult.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedCompletedAsyncResult.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/WorkflowAdditionalStores.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/GlobalConfiguration.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ImportWorkflowCommand.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/LocalRunspaceProvider.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/NewPSWorkflowExecutionOptionCommand.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityBase.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityHostManager.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSCleanupActivity.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSRemotingActivitiesBase.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSSelfRemotingActivitiesBase.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSWorkflowHost.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PowerShellWorkflowHost.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/RuntimeBase.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/TimeBasedCache.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDebugger.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDefinition.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowInstance.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJob2.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJobSourceAdapter.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowManager.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowMetadatas.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowPerformanceCounterSetInfo.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowRuntimeCompilation.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowStatusEventArgs.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTimers.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTrackingParticipant.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowUtils.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowValidation.cs delete mode 100644 src/Microsoft.PowerShell.Workflow.ServiceCore/resources/Resources.resx diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/AssemblyInfo.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/AssemblyInfo.cs deleted file mode 100644 index 944ee1264ec..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/AssemblyInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using System.Security.Permissions; -using System.Runtime.CompilerServices; -using System.Runtime.ConstrainedExecution; -using System.Diagnostics.CodeAnalysis; - -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyInformationalVersionAttribute (@"10.0.10011.16384")] -[assembly: ReliabilityContractAttribute(Consistency.MayCorruptAppDomain, Cer.MayFail)] -[assembly: AssemblyTitle("Microsoft.PowerShell.Workflow.ServiceCore")] -[assembly: AssemblyDescription("Microsoft.PowerShell.Workflow.ServiceCore")] -[assembly:AssemblyFileVersionAttribute("3.0.0.0")] -[assembly:AssemblyVersion("3.0.0.0")] - -[module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="Resources.resources", MessageId="InlineScript")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="Resources.resources", MessageId="foreach")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope="resource", Target="Resources.resources", MessageId="suspendable")] - -[assembly:InternalsVisibleTo("M3PTestAutomation,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("M3PTestCommon,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("VSTS.M3PUnitTest,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("VSTS.ActivityTests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("ActivityTests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100eb57e8072e5fa11a7783bde0003e38bec8272f81d965c161b33b2f45404c9c9177a682ee7049400723fc456ea69b638514943375f7c594027477f9d17e8c3535e6491f382308fbf1c7795413456ac88c0c9881ef49ab9789919f5eeabc9149ccb800bbef8f04103ea24b9be1fb923006b0a272c0d8d928bfdbe947a0f3df4a99")] -[assembly:InternalsVisibleTo("WorkflowCoreTest,PublicKey=0024000004800000940000000602000000240000525341310004000001000100eb57e8072e5fa11a7783bde0003e38bec8272f81d965c161b33b2f45404c9c9177a682ee7049400723fc456ea69b638514943375f7c594027477f9d17e8c3535e6491f382308fbf1c7795413456ac88c0c9881ef49ab9789919f5eeabc9149ccb800bbef8f04103ea24b9be1fb923006b0a272c0d8d928bfdbe947a0f3df4a99")] -[assembly:InternalsVisibleTo("WorkflowJobsTest,PublicKey=0024000004800000940000000602000000240000525341310004000001000100eb57e8072e5fa11a7783bde0003e38bec8272f81d965c161b33b2f45404c9c9177a682ee7049400723fc456ea69b638514943375f7c594027477f9d17e8c3535e6491f382308fbf1c7795413456ac88c0c9881ef49ab9789919f5eeabc9149ccb800bbef8f04103ea24b9be1fb923006b0a272c0d8d928bfdbe947a0f3df4a99")] - diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityCategories.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityCategories.cs deleted file mode 100644 index 5f438fae44b..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityCategories.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Activities; -using System.ComponentModel; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Collections.ObjectModel; -using System.ComponentModel.Design; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Category attribute for "Activity-Specific Parameters" - /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] - public sealed class ParameterSpecificCategoryAttribute : CategoryAttribute - { - /// - /// Creates the attribute. - /// - public ParameterSpecificCategoryAttribute() : base("") { } - - /// - /// Gets a localized version of the attribute description. - /// - /// Not used. - /// A localized version of the attribute description - protected override string GetLocalizedString(string value) - { - return Resources.ActivityParameterGroup; - } - } - - /// - /// Category attribute for "Input and Output" - /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] - public sealed class InputAndOutputCategoryAttribute : CategoryAttribute - { - /// - /// Creates the attribute. - /// - public InputAndOutputCategoryAttribute() : base("") { } - - /// - /// Gets a localized version of the attribute description. - /// - /// Not used. - /// A localized version of the attribute description - protected override string GetLocalizedString(string value) - { - return Resources.InputAndOutputGroup; - } - } - - /// - /// Category attribute for "Behavior" - /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] - public sealed class BehaviorCategoryAttribute : CategoryAttribute - { - /// - /// Creates the attribute. - /// - public BehaviorCategoryAttribute() : base("") { } - - /// - /// Gets a localized version of the attribute description. - /// - /// Not used. - /// A localized version of the attribute description - protected override string GetLocalizedString(string value) - { - return CategoryAttribute.Behavior.Category; - } - } - - /// - /// Category attribute for "Connectivity" - /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] - public sealed class ConnectivityCategoryAttribute : CategoryAttribute - { - /// - /// Creates the attribute. - /// - public ConnectivityCategoryAttribute() : base("") { } - - /// - /// Gets a localized version of the attribute description. - /// - /// Not used. - /// A localized version of the attribute description - protected override string GetLocalizedString(string value) - { - return Resources.ConnectivityGroup; - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostManager.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostManager.cs deleted file mode 100644 index 34efb708c37..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostManager.cs +++ /dev/null @@ -1,673 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.Management.Automation; -using Microsoft.PowerShell.Activities; -using System.Management.Automation.Tracing; -using System.Activities; -using System.Management.Automation.PerformanceData; -using System.Management.Automation.Remoting; - -namespace Microsoft.PowerShell.Workflow -{ - internal class ConnectionAsyncResult : IAsyncResult - { - #region Private Members - - private readonly object _state; - private readonly AsyncCallback _callback; - private bool _isCompleted; - private ManualResetEvent _completedWaitHandle; - private readonly object _syncObject = new object(); - private Exception _exception; - private readonly Guid _ownerId; - #endregion Private Members - - #region Internal - - internal ConnectionAsyncResult(object state, AsyncCallback callback, Guid ownerId) - { - _state = state; - _ownerId = ownerId; - _callback = callback; - } - - internal Object State - { - get { return _state; } - } - - internal AsyncCallback Callback - { - get { return _callback; } - } - - internal ActivityInvoker Invoker { get; set; } - - /// - /// this method is not thread safe - /// - internal void SetAsCompleted(Exception exception) - { - if (_isCompleted) return; - - lock (_syncObject) - { - if (_isCompleted) return; - - _isCompleted = true; - _exception = exception; - if (_completedWaitHandle != null) _completedWaitHandle.Set(); - } - - // invoke callback if available - if (null != _callback) - _callback(this); - } - - internal Guid OwnerId - { - get { return _ownerId; } - } - - internal void EndInvoke() - { - AsyncWaitHandle.WaitOne(); - AsyncWaitHandle.Close(); - _completedWaitHandle = null; // allow early GC - - if (null != _exception) throw _exception; - } - - #endregion Internal - - #region Overrides - - /// - /// Whether the operation represented by this method is - /// completed - /// - public bool IsCompleted - { - get { return _isCompleted; } - } - - /// - /// Wait Handle for the operation - /// - public WaitHandle AsyncWaitHandle - { - get - { - if(null == _completedWaitHandle) - { - lock(_syncObject) - { - if (null == _completedWaitHandle) - { - _completedWaitHandle = new ManualResetEvent(_isCompleted); - } - } - } - - return _completedWaitHandle; - } - } - - /// - /// Optional user specified state - /// - public object AsyncState - { - get { return _state; } - } - - /// - /// Whether this operation completed synchronously - /// - public bool CompletedSynchronously - { - get { return _completedSynchronously; } - set { _completedSynchronously = value; } - } - - private bool _completedSynchronously; - - #endregion Overrides - } - - /// - /// Activity Host Manager which will spawn a set of activity - /// host processes until all of them are used after which - /// it will start queueing requests - /// - /// Whether this class needs to remain public should be - /// evaluated - internal sealed class PSOutOfProcessActivityController : PSActivityHostController - { - #region Private Members - - /// - /// Stack of available host processes - /// - private readonly Collection _hostProcesses = new Collection(); - - /// - /// Queue of requests - /// - private readonly ConcurrentQueue _requests = - new ConcurrentQueue(); - private int _isServicing; - private const int Servicing = 1; - private const int NotServicing = 0; - private const int MinActivityHosts = 1; - private int _busyHosts; - private readonly Tracer _structuredTracer = new Tracer(); - private readonly PSWorkflowConfigurationProvider _configuration; - private static readonly PSPerfCountersMgr PerfCountersMgr = PSPerfCountersMgr.Instance; - - - /// - /// List of requests that need to be invoked again due to process crash - /// - private readonly ConcurrentQueue _failedRequests = new ConcurrentQueue(); - - #endregion Private Members - - #region Internal Methods - - internal void RunPowerShellInActivityHost(System.Management.Automation.PowerShell powershell, PSDataCollection input, - PSDataCollection output, PSActivityEnvironment policy, ConnectionAsyncResult asyncResult) - { - ActivityInvoker invoker = - new ActivityInvoker - { - Input = input, - Output = output, - Policy = policy, - PowerShell = powershell, - AsyncResult = asyncResult - }; - - _requests.Enqueue(invoker); - CheckAndStartServicingThread(); - } - - /// - /// This is a helper method which should only be called - /// from test code to reset stuff - /// - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is called from the tests")] - internal void Reset() - { - foreach (ActivityHostProcess process in _hostProcesses) - { - process.Dispose(); - } - _hostProcesses.Clear(); - - InitializeActivityHostProcesses(); - } - - #endregion Internal Methods - - #region Constructors - - internal PSOutOfProcessActivityController(PSWorkflowRuntime runtime) - : base(runtime) - { - if (runtime == null) - throw new ArgumentNullException("runtime"); - - Debug.Assert(runtime.Configuration != null, "For now only expecting PSWorkflowConfigurationProvider"); - - this._configuration = runtime.Configuration; - InitializeActivityHostProcesses(); - } - - #endregion Constructors - - #region Private Methods - - private void InitializeActivityHostProcesses() - { - // create the minimum number of hosts - for (int i = 0; i < MinActivityHosts; i++) - { - ActivityHostProcess process = CreateNewActivityHostProcess(); - _hostProcesses.Add(process); - } - } - - private ActivityHostProcess CreateNewActivityHostProcess() - { - ActivityHostProcess process = new ActivityHostProcess(_configuration.ActivityProcessIdleTimeoutSec, _configuration.LanguageMode); - process.ProcessCrashed += ProcessCrashed; - process.Finished += ProcessFinished; - process.OnProcessIdle += ProcessIdle; - - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrProcessesPoolSize); - return process; - } - - private void ProcessCrashed(object sender, ActivityHostCrashedEventArgs e) - { - // the process crashed, we need to not use it anymore - ActivityHostProcess process = sender as ActivityHostProcess; - Debug.Assert(process != null, "ActivityHostProcess did not raise event correctly"); - Debug.Assert(process.Busy, "When ProcessCrashed is raised busy should not be reset"); - process.MarkForRemoval = true; - - if (e.FailureOnSetup) - { - // the request needs to be processed again - _failedRequests.Enqueue(e.Invoker); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsPerSec); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsQueueLength); - } - - // Below call is added to fix the race condition: - // When OOP host process is crashed, ProcessCrashed() method sets the process as MarkForRemoval, context switch happened at this time - // and another process has finished and started the servicing thread, which checks the above process as MarkForRemoval and disposes it. - // ProcessFinished event handler is unregistered from the process and ActivityHostProcess.HandleTransportError will not be able to raise - // process finished event, resulting inconsistent _busyHost count. - // - DecrementHostCountAndStartThreads(); - } - - private void ProcessFinished(object sender, EventArgs e) - { - DecrementHostCountAndStartThreads(); - } - - private void DecrementHostCountAndStartThreads() - { - Interlocked.Decrement(ref _busyHosts); - - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrBusyProcessesCount, - -1); - - CheckAndStartServicingThread(); - } - - private void ProcessIdle(object sender, EventArgs e) - { - // Only service thread should access the hostProcess collection - // Start servicing thread if it is not running already - // - CheckAndStartServicingThread(); - } - - /// - /// Handler method which runs a specific command in the specified process - /// - /// - private static void RunPowerShellInActivityHostWorker(object state) - { - // Tuple object with HostProcess and ActivityInvoker is added to ThreadPool.QueueUserWorkItem as part of RunInProcess() - // Back reference on ActivityHostProcess in ActivityInvoker is removed - // - Tuple tupleProcessAndInvoker = state as Tuple; - tupleProcessAndInvoker.Item1.PrepareAndRun(tupleProcessAndInvoker.Item2); - } - - /// - /// Checks to see if a thread is servicing requests. If not - /// starts one - /// - private void CheckAndStartServicingThread() - { - if (Interlocked.CompareExchange(ref _isServicing, Servicing, NotServicing) == NotServicing) - { - ThreadPool.QueueUserWorkItem(ServiceRequests); - } - } - - /// - /// Method which performs the actual servicing of requests - /// - /// - private void ServiceRequests(object state) - { - bool isFailedRequest = false; - while (Interlocked.CompareExchange(ref _busyHosts, _configuration.MaxActivityProcesses, _configuration.MaxActivityProcesses) < _configuration.MaxActivityProcesses) - { - // if there are any processes marked for removal - // remove them - List toRemove = _hostProcesses.Where(process => process.MarkForRemoval).ToList(); - - foreach(var process in toRemove) - { - SafelyDisposeProcess(process); - } - - ActivityInvoker invoker; - // first service previously failed request - // and then a queued request - if (_failedRequests.Count > 0) - { - _failedRequests.TryDequeue(out invoker); - isFailedRequest = true; - } - else - { - _requests.TryDequeue(out invoker); - isFailedRequest = false; - } - - if (invoker == null) break; - - if (invoker.IsCancelled) continue; - - if (isFailedRequest) - { - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsQueueLength, - -1); - } - else - { - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrPendingRequestsQueueLength, - -1); - } - - bool processed = false; - foreach (ActivityHostProcess process in _hostProcesses.Where(process => !process.Busy)) - { - // we have found the first free process available use it - processed = true; - RunInProcess(invoker, process); - break; - } - - // if there weren't enough processes, then we create one more - if (processed) continue; - - ActivityHostProcess hostProcess = CreateNewActivityHostProcess(); - _hostProcesses.Add(hostProcess); - RunInProcess(invoker, hostProcess); - } - - // we are all done, set servicing to false - Interlocked.CompareExchange(ref _isServicing, NotServicing, Servicing); - - // Try to start the servicing thread again if there are any pending requests and activity host processes are available. - // This is required to fix the race condition between CheckAndStartServicingThread() and ServiceRequests() methods. - // - if((_failedRequests.Count > 0 || _requests.Count > 0) && - Interlocked.CompareExchange(ref _busyHosts, _configuration.MaxActivityProcesses, _configuration.MaxActivityProcesses) < _configuration.MaxActivityProcesses) - { - CheckAndStartServicingThread(); - } - } - - - /// - /// Unregisters all wait handles and disposes a process - /// - /// - private void SafelyDisposeProcess(ActivityHostProcess process) - { - process.Finished -= ProcessFinished; - process.ProcessCrashed -= ProcessCrashed; - process.OnProcessIdle -= ProcessIdle; - - process.Dispose(); - - _hostProcesses.Remove(process); - - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrProcessesPoolSize, - -1); - } - - /// - /// Method called by servicing thread. This method will run the command in the - /// specified process on a separate thread - /// - /// - /// - private void RunInProcess(ActivityInvoker invoker, ActivityHostProcess process) - { - if (invoker.IsCancelled) - { - return; - } - - process.Busy = true; - Interlocked.Increment(ref _busyHosts); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrBusyProcessesCount); - - // Adding Tuple object with HostProcess and ActivityInvoker to ThreadPool.QueueUserWorkItem - // Back reference on ActivityHostProcess in ActivityInvoker is removed - // - Tuple tupleProcessAndInvoker = new Tuple(process, invoker); - ThreadPool.QueueUserWorkItem(RunPowerShellInActivityHostWorker, tupleProcessAndInvoker); - } - - #endregion Private Methods - - #region PSActivityHostManager Overrides - - /// - /// Begin invocation of command specified in activity - /// - /// pipeline of command to execute - /// input collection - /// output collection - /// policy to use for the activity - /// optional callback - /// optional caller specified state - /// IAsyncResult - internal IAsyncResult BeginInvokePowerShell(System.Management.Automation.PowerShell command, - PSDataCollection input, PSDataCollection output, PSActivityEnvironment policy, - AsyncCallback callback, object state) - { - if (command == null) - { - throw new ArgumentNullException("command"); - } - - ConnectionAsyncResult result = new ConnectionAsyncResult(state, callback, command.InstanceId); - - _structuredTracer.OutOfProcessRunspaceStarted(command.ToString()); - - ActivityInvoker invoker = - new ActivityInvoker - { - Input = input, - Output = output, - Policy = policy, - PowerShell = command, - AsyncResult = result - }; - - result.Invoker = invoker; - - _requests.Enqueue(invoker); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrIncomingRequestsPerSec); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrPendingRequestsQueueLength); - CheckAndStartServicingThread(); - return result; - } - - /// - /// Block until operation is complete on the current thread - /// - /// IAsyncResult to block on - internal void EndInvokePowerShell(IAsyncResult asyncResult) - { - ConnectionAsyncResult result = asyncResult as ConnectionAsyncResult; - - if (result == null) - { - throw new PSInvalidOperationException(Resources.AsyncResultNotValid); - } - - result.EndInvoke(); - } - - /// - /// Cancels an already started execution - /// - /// async result to cancel - internal void CancelInvokePowerShell(IAsyncResult asyncResult) - { - ConnectionAsyncResult result = asyncResult as ConnectionAsyncResult; - if (result != null) - { - result.Invoker.StopPowerShell(); - } - } - - #endregion PSActivityHostManager Overrides - } - - #region ActivityInvoker - - internal class ActivityInvoker - { - internal PSDataCollection Input { get; set; } - internal PSDataCollection Output { get; set; } - internal System.Management.Automation.PowerShell PowerShell { get; set; } - internal PSActivityEnvironment Policy { get; set; } - internal ConnectionAsyncResult AsyncResult { get; set; } - private readonly object _syncObject = new object(); - private bool _invoked; - private bool _cancelled; - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - internal void InvokePowerShell(Runspace runspace) - { - IAsyncResult result = null; - - if (_cancelled) - return; - - Exception invokeException = null; - - try - { - // Acquiring sync object to fix a race condition between BeginInvoke() and Stop() operations on powershell object. - // Invoked flag is set only when begin invoke is succeeded, so that if this activity is cancelled, powershell needs to be stopped in StopPowerShell(). - // - lock (_syncObject) - { - if (_cancelled) - return; - - _tracer.WriteMessage("State of runspace passed to invoker ", runspace.RunspaceStateInfo.State.ToString()); - - // at this point we assume we have a clean runspace to - // run the command - // if the runspace is broken then the invocation will - // result in an error either ways - PowerShell.Runspace = runspace; - - // Temporary fix: - // If we are about to run this out of proc, we need to complete any open - // input. Otherwise, the out-of-proc PowerShell will hang expecting input. - // However, this has a bug that it will break a third command trying to - // use the same input collection. Nana to resolve with final fix. - if ((Input != null) && Input.EnumeratorNeverBlocks && Input.IsOpen) - { - Input.Complete(); - } - - _tracer.WriteMessage("BEGIN invocation of command out of proc"); - result = Output == null - ? PowerShell.BeginInvoke(Input) - : PowerShell.BeginInvoke(Input, Output); - - // _invoked should bet set here as powershell object can be disposed before runspace assignment due to cancelling an activity - // - _invoked = true; - } // end of lock - - // wait until PowerShell Execution Completes - PowerShell.EndInvoke(result); - _tracer.WriteMessage("END invocation of command out of proc"); - } - catch (Exception exception) - { - // Since the callback is internally handled there should - // not be any exceptions. Fix the same - // ignore any exceptions caused in the callback invocation - _tracer.WriteMessage("Running powershell in activity host threw exception"); - _tracer.TraceException(exception); - invokeException = exception; - - // transport errors are centrally handled for queue and process management - if (exception is PSRemotingTransportException) throw; - } - finally - { - AsyncResult.SetAsCompleted(invokeException); - } - } - - internal void StopPowerShell() - { - bool needToStop = false; - - lock (_syncObject) - { - _cancelled = true; - - if (_invoked) - { - needToStop = true; - } - } - - if (needToStop) - { - PowerShell.Stop(); - } - - AsyncResult.SetAsCompleted(null); - } - - internal bool IsCancelled - { - get - { - lock (_syncObject) - { - return _cancelled; - } - } - } - } - - #endregion ActivityInvoker - -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostProcess.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostProcess.cs deleted file mode 100644 index 83ab8a54339..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityHostProcess.cs +++ /dev/null @@ -1,564 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Remoting; -using System.Management.Automation.Tracing; -using System.Management.Automation.PerformanceData; -using System.Timers; -using Timer = System.Timers.Timer; -using System.Globalization; - -namespace Microsoft.PowerShell.Workflow -{ - internal class ActivityHostCrashedEventArgs : EventArgs - { - internal bool FailureOnSetup { get; set; } - internal ActivityInvoker Invoker { get; set; } - } - - /// - /// Encapsulates an out of process activity host - /// - /// This class is not thread safe. Caller has to - /// ensure thread safety of accessing internal properties - internal class ActivityHostProcess : IDisposable - { - #region Private Members - private static PSPerfCountersMgr _perfCountersMgr = PSPerfCountersMgr.Instance; - private Runspace _runspace; - private static readonly WSManConnectionInfo ActivityHostConnectionInfo; - private const string ActivityHostShellUri = "http://schemas.microsoft.com/powershell/Microsoft.PowerShell.Workflow.ActivityHost"; - private static readonly string[] ActivitiesTypesFiles = new[] { - @"%windir%\system32\windowspowershell\v1.0\modules\psworkflow\PSWorkflow.types.ps1xml" - }; - private static readonly TypeTable ActivitiesTypeTable; - private const int WSManLocalPort = 47001; - private bool _busy; - private readonly object _syncObject = new object(); - private readonly PowerShellProcessInstance _processInstance; - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private ActivityInvoker _currentInvoker; - - /// - /// This the period of time for which the process will - /// remain inactive. Afterwards it will be killed - /// - private const int TimeOut = 5*60*1000; - private readonly Timer _timer; - - /// - /// Use this flag to flip between IPC process and - /// WSMan process in localhost - /// - private readonly bool _useJobIPCProcess; - - private readonly PSLanguageMode? _languageMode; - - /// - /// Creating a _PSSetVariable to set multiple variables in a single call --> PERF - /// Naming it _PSSetVariable to avoid name collision with Set-Variable cmdlet - /// - private const string SetVariableFunction = @"function _PSSetVariable - { - [CmdletBinding()] - param( - - [Parameter(Position=0)] - [string[]] - $Name, - - [Parameter(Position=1)] - [object[]] - $Value - ) - - for($i=0; $i -lt $Name.Count; $i++) - { - microsoft.powershell.utility\set-variable -name $Name[$i] -value $Value[$i] -scope global - } - - Set-StrictMode -Off - }"; - - private bool _isDisposed; - #endregion Private Members - - #region Constructors - - /// - /// Initialize a connection info object in the static constructor - /// It can be reused for all connections - /// - static ActivityHostProcess() - { - ActivityHostConnectionInfo = new WSManConnectionInfo { Port = WSManLocalPort, ShellUri = ActivityHostShellUri }; - - List typefiles = TypeTable.GetDefaultTypeFiles(); - typefiles.AddRange(ActivitiesTypesFiles.Select(Environment.ExpandEnvironmentVariables)); - ActivitiesTypeTable = new TypeTable(typefiles); - - } - - internal ActivityHostProcess(int activityHostTimeoutSec, PSLanguageMode? languageMode) - { - _languageMode = languageMode; - _useJobIPCProcess = true; - _tracer.WriteMessage("BEGIN Creating new PowerShell process instance"); - _processInstance = new PowerShellProcessInstance(); - _tracer.WriteMessage("END Creating new PowerShell process instance "); - _runspace = CreateRunspace(); - _tracer.WriteMessage("New runspace created ", _runspace.InstanceId.ToString()); - _timer = new Timer {AutoReset = false, Interval = TimeOut}; - _timer.Elapsed += TimerElapsed; - _timer.Interval = activityHostTimeoutSec > 0 ? activityHostTimeoutSec*1000 : TimeOut; - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrCreatedProcessesCount); - } - - #endregion Constructors - - #region Private Methods - - /// - /// Get the runspace corresponding to this process - /// - /// indicates if a new runspace - /// needs to be created - /// runspace object - private Runspace GetRunspace(bool createNew) - { - if (_runspace.RunspaceStateInfo.State == RunspaceState.BeforeOpen) - { - // if the runspace is not opened, open it and return - OpenRunspace(_runspace); - } - else - { - if (_useJobIPCProcess) - { - // dispose the existing runspace and create a new one - CloseAndDisposeRunspace(); - - _runspace = CreateRunspace(); - _tracer.WriteMessage("New runspace created ", _runspace.InstanceId.ToString()); - OpenRunspace(_runspace); - } - } - return _runspace; - } - - private void CloseAndDisposeRunspace() - { - try - { - // Close and dispose the existing runspace - // - _runspace.Close(); - _runspace.Dispose(); - } - catch (Exception e) - { - // RemoteRunspace.Close can throw exceptions when Server process has exited or runspace is invalid. - // Ignoring all exceptions as this runspace was used for previous OOP activity execution. - // - _tracer.TraceException(e); - - // do nothing - } - } - - /// - /// Depending on the option return an Out-of-proc - /// or remoting runspace on localhost - /// - /// runspace object for use - private Runspace CreateRunspace() - { - return _useJobIPCProcess ? RunspaceFactory.CreateOutOfProcessRunspace(ActivitiesTypeTable, _processInstance) : RunspaceFactory.CreateRunspace(ActivityHostConnectionInfo, null, ActivitiesTypeTable); - } - - /// - /// Opens the specified runspace. If there are any errors in - /// opening the runspace, the method just eats them so that - /// an unhandled exception in the background thread in which this - /// method is invoked does not lead to a process crash - /// - /// runspace to open - private void OpenRunspace(Runspace runspace) - { - // a runspace open can fail for a variety of reasons - // eat the exceptions - try - { - _tracer.WriteMessage("Opening runspace " , _runspace.InstanceId.ToString()); - runspace.Open(); - _tracer.WriteMessage("Runspace opened successfully ", _runspace.InstanceId.ToString()); - - if (_languageMode != null && _languageMode.HasValue) - { - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - string langScript = "$ExecutionContext.SessionState.LanguageMode = '" + _languageMode.Value.ToString() + "'"; - ps.AddScript(langScript); - ps.Invoke(); - } - } - } - catch (PSRemotingTransportRedirectException) - { - // we should not be getting this exception - // in the normal case - _tracer.WriteMessage("Opening runspace threw PSRemotingTransportRedirectException", _runspace.InstanceId.ToString()); - Debug.Assert(false, "We should not get a redirect exception under normal circumstances"); - } - catch (PSRemotingTransportException transportException) - { - _tracer.WriteMessage("Opening runspace threw PSRemotingTransportException", _runspace.InstanceId.ToString()); - _tracer.TraceException(transportException); - // throwing PSRemotingTransportException exception as it will be handled at single place in PrepareAndRun() method. - throw; - } - catch(PSRemotingDataStructureException) - { - // just eat the exception - _tracer.WriteMessage("Opening runspace threw PSRemotingDataStructureException", _runspace.InstanceId.ToString()); - Debug.Assert(false, "We should not get a protocol exception under normal circumstances"); - } - } - - /// - /// Import all the specified modules in the specified runspace - /// - /// runspace in which to import the modules - /// collection of modules to import - /// true if setting up of runspace from policy succeeded - private void ImportModulesFromPolicy(Runspace runspace, ICollection modules) - { - // If any modules are specified, load them into the runspace. - // In general, autoloading should be preferred to explicitly loading a module - // at startup time... - if (modules.Count <= 0) - return; - - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - // Setting erroraction to stop for import-module since they are required modules. If not present, stop the execution - ps.AddCommand("Import-Module").AddArgument(modules).AddParameter("ErrorAction",ActionPreference.Stop).AddParameter("Force"); - ps.Invoke(); - } - } - - /// - /// Set the specified variables in the runspace - /// - /// runspace in which the variables need - /// to be set - /// collection of variables that need to be set - private void SetVariablesFromPolicy(Runspace runspace, IDictionary variables) - { - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - ps.AddScript(SetVariableFunction); - ps.Invoke(); - - ps.Commands.Clear(); - ps.AddCommand("_PSSetVariable").AddParameter("Name", variables.Keys).AddParameter("Value", variables.Values); - ps.Invoke(); - - // Remove the temporary function _PSSetVariable after its use is done. - ps.Commands.Clear(); - ps.AddCommand("Remove-Item").AddParameter("Path", "function:\\_PSSetVariable").AddParameter("Force"); - ps.Invoke(); - - } - } - - /// - /// Set the busy flag since the process is being used. - /// Stop the idle timer - /// - private void SetBusy() - { - lock(_syncObject) - { - _busy = true; - - // stop the idle timer - _timer.Stop(); - } - } - - /// - /// Reset the busy flag and start the idle timer - /// - private void ResetBusy() - { - lock(_syncObject) - { - // Process state should not be set to false if it is already marked for removal - // Process is marked for removal in two cases: - // 1) When PSRemotingTransportException is received in PrepareAndRun, in this case Server process is already crashed/exited. - // If busy state is set to false, this process can be assigned to another activity by the servicing thread. - // Instead of adding separate boolean variable in PrepareAndRun to call ResetBusy we are checking for not marked for removal here. - // 2) When idle time of 5 min elapsed, process is marked for removal so that server process can be killed. After this, ResetBusy is never called. - // - if (_busy && !_markForRemoval) - { - _busy = false; - - // enable the idle timer - _timer.Enabled = true; - - this.RaiseProcessFinishedEvent(); - } - } - } - - /// - /// Idle timeout has occured. If the runspace is not being used - /// just close it - /// - /// sender of this event - /// unused - private void TimerElapsed(object sender, ElapsedEventArgs e) - { - lock (_syncObject) - { - if (_busy) return; - - // Mark this process for removal and set it as busy so that this process will not be assigned to any new activity. - // Marking for removal will ensure that servicing thread will remove this object from host process collection. - // - _busy = true; - _markForRemoval = true; - } - - if (OnProcessIdle != null) - OnProcessIdle(this, new EventArgs()); - } - - /// - /// Handles a transport error since it is most likely that - /// the process crash - /// - /// the transport exception - /// that was raised - /// true indicates that the crash was - /// encountered when setting up the process and not when the - /// command was run - private void HandleTransportError(PSRemotingTransportException transportException, bool onSetup) - { - _tracer.TraceException(transportException); - - if (ProcessCrashed != null) - { - ActivityHostCrashedEventArgs eventArgs = new ActivityHostCrashedEventArgs - {FailureOnSetup = onSetup, - Invoker = _currentInvoker}; - ProcessCrashed(this, eventArgs); - } - } - - internal void RaiseProcessFinishedEvent() - { - if (Finished != null) - Finished(this, new EventArgs()); - } - - #endregion Private Methods - - #region Internal Methods - - internal static String ActivityHostConfiguration - { - get { return ActivityHostShellUri; } - } - internal event EventHandler Finished; - internal event EventHandler OnProcessIdle; - - internal event EventHandler ProcessCrashed; - - /// - /// Prepare the environment based on the policy and run the powershell - /// - /// - /// It is assumed that the caller of this method will spawn appropriate - /// thread and so it is fine for us to call the callback on the same thread - internal void PrepareAndRun(ActivityInvoker invoker) - { - bool setupSucceeded = false; - try - { - // Transport (PSRemotingTransportException) error can happen during setup/prepare phase, so it is getting assigned before setup phase. - // - _currentInvoker = invoker; - Runspace runspace = null; - // Retry for 10 times if runspace is in Broken state - // - // Runspace can be broken, when a remote runspace is getting closed during the next activity execution, - // the close ack is getting timedout and that close ack is received by the newly created remote runspace for - // the next activity since stdoutput stream of server process is not cleared/discarded, - // that is why during new runspace is in broken state while it is being opened. - // - for (int i = 1; (i <= 10) && !invoker.IsCancelled; i++) - { - // prepare the runspace with the necessary - // modules and variables here - runspace = GetRunspace(true); - - if (runspace.RunspaceStateInfo.State == RunspaceState.Opened) - break; - - System.Threading.Thread.Sleep(i*200); - } - - if (invoker.IsCancelled) - { - return; - } - - Debug.Assert((runspace.RunspaceStateInfo.State == RunspaceState.Opened), "Runspace is not in Opened state for running an OOP activity"); - - if (invoker.Policy.Variables.Count > 0) - { - // set the variables in the specified runspace - _tracer.WriteMessage("BEGIN Setting up variables in runspace ", _runspace.InstanceId.ToString()); - SetVariablesFromPolicy(runspace, invoker.Policy.Variables); - _tracer.WriteMessage("END Setting up variables in runspace ", _runspace.InstanceId.ToString()); - } - - if (invoker.Policy.Modules.Count > 0) - { - // attempt loading the modules in the specified runspace - _tracer.WriteMessage("BEGIN Setting up runspace from policy ", _runspace.InstanceId.ToString()); - ImportModulesFromPolicy(runspace, invoker.Policy.Modules); - _tracer.WriteMessage("END Setting up runspace from policy ", _runspace.InstanceId.ToString()); - } - - // Prepare phase is completed without any issues. - // setupSucceeded flag is used in HandleTransportError method to enqueue the current activity for retry. - // If there is any PSRemotingTransportException during InvokePowershell current activity will not be enqueued to setup failed requests in ActivityHostManager. - // - setupSucceeded = true; - - // at this point we assume we have a clean runspace to - // run the command - // if the runspace is broken then the invocation will - // result in an error either ways - invoker.InvokePowerShell(runspace); - } - catch (PSRemotingTransportException transportException) - { - HandleTransportError(transportException, !setupSucceeded); - } - catch (Exception e) - { - // at this point there is an exception other than a - // transport exception that is caused by trying to - // setup the runspace. Release the asyncresult - // accordingly - _tracer.TraceException(e); - invoker.AsyncResult.SetAsCompleted(e); - } - finally - { - _currentInvoker = null; - ResetBusy(); - } - } - - /// - /// If the process represented by this object is busy - /// - internal bool Busy - { - get - { - lock (_syncObject) - { - return _busy; - } - } - set - { - if (value) SetBusy(); - else ResetBusy(); - } - } - - /// - /// Indicates that the associated process crashed and this object - /// needs to be removed by ActivityHostManager - /// - internal bool MarkForRemoval { - get - { - // _processInstance.HasExited can not be used to tell as marked for removal - // as it will cause inconsistent busy host count - // - return _markForRemoval; - } - set - { - _markForRemoval = value; - } - } - private bool _markForRemoval = false; - - #endregion Internal Methods - - #region IDisposable - - /// - /// Dispose - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// dispose of managed resources - /// - /// true if being disposed - protected void Dispose(bool disposing) - { - if (!disposing) return; - - if (_isDisposed) return; - lock (_syncObject) - { - if (_isDisposed) return; - _isDisposed = true; - } - - Debug.Assert(_runspace != null, "Runspace is already null"); - - CloseAndDisposeRunspace(); - - _processInstance.Dispose(); - - _currentInvoker = null; - _tracer.Dispose(); - - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ActivityHostMgrDisposedProcessesCount); - } - - #endregion IDisposable - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityUtils.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityUtils.cs deleted file mode 100644 index ccc5af33447..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ActivityUtils.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Management.Automation; -using System.Management.Automation.Remoting; -using System.Management.Automation.Runspaces; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - static class ActivityUtils - { - // Extension method to add IsNullOrEmpty to arrays. - internal static bool IsNullOrEmpty(this System.Collections.ICollection c) - { - return (c == null || c.Count == 0); - } - - private static int DefaultMaximumConnectionRedirectionCount = 5; - - internal static List GetConnectionInfo(string[] PSComputerName, string[] PSConnectionUri, - string PSCertificateThumbprint, string PSConfigurationName, - bool? PSUseSsl, uint? PSPort, string PSApplicationName, - PSCredential PSCredential, AuthenticationMechanism PSAuthentication, - bool PSAllowRedirection, System.Management.Automation.Remoting.PSSessionOption options) - { - List connections = new List(); - - string[] machineList = null; - bool connectByComputerName = false; - - // Connect by computername - if ((! PSComputerName.IsNullOrEmpty()) && (PSConnectionUri.IsNullOrEmpty())) - { - machineList = PSComputerName; - connectByComputerName = true; - } - else if ((PSComputerName.IsNullOrEmpty()) && (! PSConnectionUri.IsNullOrEmpty())) - { - machineList = PSConnectionUri; - } - else - { - throw new ArgumentException(Resources.CannotSupplyUriAndComputername); - } - - // Go through each machine in the list an update its properties - foreach (string machine in machineList) - { - if (!string.IsNullOrEmpty(machine)) - { - WSManConnectionInfo connectionInfo = new WSManConnectionInfo(); - - if (PSPort.HasValue) - { - connectionInfo.Port = (int)PSPort.Value; - } - - if (PSUseSsl.HasValue && (PSUseSsl.Value)) - { - connectionInfo.Scheme = WSManConnectionInfo.HttpsScheme; - } - - if (!String.IsNullOrEmpty(PSConfigurationName)) - { - connectionInfo.ShellUri = PSConfigurationName; - } - - if (!String.IsNullOrEmpty(PSApplicationName)) - { - connectionInfo.AppName = PSApplicationName; - } - - if (connectByComputerName) - { - connectionInfo.ComputerName = machine; - } - else - { - connectionInfo.ConnectionUri = (Uri)LanguagePrimitives.ConvertTo(machine, typeof(Uri), System.Globalization.CultureInfo.InvariantCulture); - } - - if (PSCredential != null) - { - connectionInfo.Credential = PSCredential; - } - - if (!String.IsNullOrEmpty(PSCertificateThumbprint)) - { - connectionInfo.CertificateThumbprint = PSCertificateThumbprint; - } - - if (PSAuthentication != AuthenticationMechanism.Default) - { - connectionInfo.AuthenticationMechanism = PSAuthentication; - } - - connectionInfo.MaximumConnectionRedirectionCount = PSAllowRedirection ? DefaultMaximumConnectionRedirectionCount : 0; - - if (options != null) - { - connectionInfo.SetSessionOptions(options); - } - - connections.Add(connectionInfo); - } - else - { - // add a null connection to account for "" or $null in PSComputerName parameter - connections.Add(null); - } - } - - return connections; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ConnectionManager.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ConnectionManager.cs deleted file mode 100644 index 93404b4c62e..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ConnectionManager.cs +++ /dev/null @@ -1,2372 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.Timers; -using Timer = System.Timers.Timer; -using Microsoft.PowerShell.Activities; -using System.Management.Automation.Tracing; -using System.Management.Automation.PerformanceData; - -namespace Microsoft.PowerShell.Workflow -{ - #region Connection - - /// - /// class the contains a remote connection (runspace) and associated - /// data for managing the same - /// - /// the only reason this class has an internal scope is for - /// test purposes. Else this can be a private class inside - /// connection manager - internal class Connection - { - private Runspace _runspace; - private bool _busy; - private bool _idle; - private readonly object _syncObject = new object(); - private static readonly EventArgs EventArgs = new EventArgs(); - private readonly Guid _instanceId = Guid.NewGuid(); - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private readonly Tracer _structuredTracer = new Tracer(); - private static readonly PSPerfCountersMgr _perfCountersMgr = PSPerfCountersMgr.Instance; - private bool _readyForDisconnect = false; - private bool _readyForReconnect = false; - private ConnectionManager _manager; - - /// - /// retry interval cannot be 0, so initial value is 1 - /// - private uint _retryInterval = 1; - - internal uint RetryAttempt { get; set; } - internal uint RetryCount { get; set; } - internal GetRunspaceAsyncResult AsyncResult { get; set; } - internal WSManConnectionInfo ConnectionInfo { get; set; } - - internal event EventHandler CloseCompleted; - internal event EventHandler OpenCompleted; - internal event EventHandler DisconnectCompleted; - internal event EventHandler ReconnectCompleted; - - private const int Open = 1; - private const int Close = 2; - private const int Disconnect = 3; - private const int Reconnect = 4; - - internal Connection(ConnectionManager manager) - { - _manager = manager; - _tracer.WriteMessage("PSW Conn: Creating new connection"); - } - - internal bool DisconnectedIntentionally { get; set; } - - internal Runspace Runspace - { - get { return _runspace; } - } - - internal Guid InstanceId - { - get { return _instanceId; } - } - - internal bool Idle - { - get - { - lock (_syncObject) - { - return _idle; - } - } - set - { - lock (_syncObject) - { - _idle = value; - } - } - } - internal bool ReadyForDisconnect - { - get - { - lock (_syncObject) - { - return _readyForDisconnect; - } - } - - set - { - lock (_syncObject) - { - _readyForDisconnect = value; - - if (_readyForDisconnect) - _readyForReconnect = false; - } - } - } - - internal bool ReadyForReconnect - { - get - { - lock (_syncObject) - { - return _readyForReconnect; - } - } - set - { - lock (_syncObject) - { - _readyForReconnect = value; - - if (_readyForReconnect) - _readyForDisconnect = false; - } - } - } - - internal bool Busy - { - get - { - lock (_syncObject) - { - return _busy; - } - } - set - { - lock (_syncObject) - { - _busy = value; - - if (_busy) - { - _idle = false; - } - - // connection is set to busy by the connection manager - // when it is assigning the connection to a new activity - // then the connection should be marked as not ready for - // disconnect since the activity has to signal the same - // when it is not busy we do not want to disconnect idle - // connections, hence it needs to be marked as not ready - // for disconnect - _readyForDisconnect = false; - _readyForReconnect = false; - } - } - } - - internal uint RetryInterval - { - get { return _retryInterval; } - set - { - _retryInterval = value; - if (_retryInterval == 0) _retryInterval = 1; - } - } - - private bool CheckAndReconnectAfterCrashOrShutdown(WSManConnectionInfo connectionInfo) - { - bool connectAsyncSucceeded = false; - - RunCommandsArguments args = this.AsyncResult.AsyncState as RunCommandsArguments; - if (args != null) - { - Guid runspaceId = args.ImplementationContext.DisconnectedRunspaceInstanceId; - - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell currentPowerShellInstance = implementationContext.PowerShellInstance; - PSActivityContext psActivityContext = args.PSActivityContext; - - // Non-empty guid represents that args.ImplementationContext.EnableRemotingActivityAutoResume is true - if (!runspaceId.Equals(Guid.Empty)) - { - Runspace[] runspaces = null; - - try - { - // Queries the server for disconnected runspaces. - // Each runspace object in the returned array is in the Disconnected state and can be - // connected to the server by calling the Connect() or ConnectAsync() methods on the runspace. - runspaces = Runspace.GetRunspaces(connectionInfo); - } - catch (System.Management.Automation.RuntimeException) - { - _tracer.WriteMessage("PSW Conn: Disconnected Remote Runspace is not available. Runspace Instance Id: " + runspaceId.ToString()); - } - - if (runspaces != null) - { - // In local machine case, remote runspace with the specific id will be not be available after machine shutdown. - // Remoterunspace on local machine may be available if only current process is crashed or suspended forcefully. - // OpenAsync() will takes care of create new remote runspace if disconnected runspace is not available. - - Runspace foundRunspace = runspaces.FirstOrDefault(currentRunspace => currentRunspace.InstanceId.Equals(runspaceId)); - - if (foundRunspace != null && foundRunspace.RunspaceStateInfo.State == RunspaceState.Disconnected) - { - System.Management.Automation.PowerShell disconnectedPowerShell = null; - - try - { - // When disconnectedPowerShell is not available PSInvalidOperationException will be thrown and finally block takes care of releasing the runspace. - // And OpenAsync() will create a new remote runspace if disconnected runspace is not available. - disconnectedPowerShell = foundRunspace.CreateDisconnectedPowerShell(); - - // Add stream handler and connect to disconnected remote runspace - PSActivity.AddHandlersToStreams(disconnectedPowerShell, args); - disconnectedPowerShell.ConnectAsync(args.Output, PSActivity.PowerShellInvocationCallback, args); - - - // Assign the disconnected powershell to ImplementationContext and dispose the existing powershell instance. - implementationContext.PowerShellInstance = disconnectedPowerShell; - - // Since we are disposing the current command, remove this command from the list - // of running commands. - lock (psActivityContext.runningCommands) - { - RetryCount retryCount = psActivityContext.runningCommands[currentPowerShellInstance]; - psActivityContext.runningCommands.Remove(currentPowerShellInstance); - psActivityContext.runningCommands[disconnectedPowerShell] = retryCount; - } - currentPowerShellInstance.Dispose(); - - _runspace = foundRunspace; - _runspace.StateChanged += RunspaceStateChanged; - - connectAsyncSucceeded = true; - } - catch (PSInvalidOperationException) - { - _tracer.WriteMessage("PSW Conn: Disconnected PowerShell is not available. Runspace Instance Id: " + foundRunspace.InstanceId.ToString()); - } - finally - { - // release the runspace if reconnect did not happen. - if (connectAsyncSucceeded == false && foundRunspace != null) - { - try - { - // Remove stream handlers on disconnectedPowerShell - // Non null disconnectedPowerShell represents that handlers were added on disconnectedPowerShell and an exception happened during ConnectAsync. - if (disconnectedPowerShell != null) - PSActivity.RemoveHandlersFromStreams(disconnectedPowerShell, args); - - foundRunspace.Close(); - foundRunspace.Dispose(); - } - catch (Exception) - { - // Runspace.Close can throw exceptions when Server process has exited or runspace is invalid. - // Ignoring all exceptions. - // - _tracer.WriteMessage("PSW Conn: Disconnected PowerShell is not available. Runspace Instance Id: " + foundRunspace.InstanceId.ToString()); - } - } - } - } - } - } - } - - return connectAsyncSucceeded; - } - - internal void OpenAsync() - { - // Always create runspace with WSManConnectionInfo.EnableNetworkAccess set to true. - WSManConnectionInfo copyConnectionInfo = ConnectionInfo.Copy(); - copyConnectionInfo.EnableNetworkAccess = true; - - // If the remote runspace is not available after crash/shutdown, - // activity task resume will fall back to reexecuting in a new runspace is assigned for the connection - if (!CheckAndReconnectAfterCrashOrShutdown(copyConnectionInfo)) - { - _runspace = RunspaceFactory.CreateRunspace(copyConnectionInfo, null, LocalRunspaceProvider.SharedTypeTable); - _runspace.StateChanged += RunspaceStateChanged; - _tracer.WriteMessage("PSW Conn: Calling OpenAsync on runspace"); - _runspace.OpenAsync(); - } - } - - internal void CloseAsync() - { - _tracer.WriteMessage("PSW Conn: Calling CloseAsync on runspace"); - - // at this point the runspace can be in a closed or broken state - // if so then close has to simply raise the event - // if it was not in a terminal state when checking but happens - // later but before CloseAsync() is called then the other - // call which caused the state change will raise an event - if (_runspace.RunspaceStateInfo.State == RunspaceState.Broken || - _runspace.RunspaceStateInfo.State == RunspaceState.Closed) - { - RaiseEvents(Close); - } - else - _runspace.CloseAsync(); - } - - internal void DisconnectAsync() - { - bool disconnect = false; - - if (_readyForDisconnect) - { - lock (_syncObject) - { - if (_readyForDisconnect) - { - _readyForDisconnect=false; - _readyForReconnect = false; - disconnect = true; - } - } - if (disconnect) - { - _tracer.WriteMessage("PSW Conn: Calling Disconnect Async"); - _manager.DisconnectCalled(); - _runspace.DisconnectAsync(); - return; - } - } - RaiseEvents(Disconnect); - } - - internal void ReconnectAsync() - { - bool reconnect = false; - - if (_readyForReconnect) - { - lock (_syncObject) - { - if (_readyForReconnect) - { - _readyForReconnect = false; - _readyForDisconnect = false; - reconnect = true; - } - } - - if (reconnect) - { - _tracer.WriteMessage("PSW Conn: Calling reconnect async"); - _manager.ReconnectCalled(); - _runspace.ConnectAsync(); - return; - } - } - RaiseEvents(Reconnect); - } - - private void RunspaceStateChanged(object sender, RunspaceStateEventArgs e) - { - _structuredTracer.RunspaceStateChanged(this._instanceId.ToString(), e.RunspaceStateInfo.State.ToString(), string.Empty); - _tracer.WriteMessage("PSW Conn: runspace state" + e.RunspaceStateInfo.State.ToString()); - switch (e.RunspaceStateInfo.State) - { - case RunspaceState.Opening: - return; - - case RunspaceState.Closing: - return; - - case RunspaceState.Disconnecting: - { - ReadyForDisconnect = false; - } - return; - - case RunspaceState.Opened: - { - // runspace opened successfully, assign it to the asyncresult - // reset retry counter - RetryAttempt = 0; - ReadyForReconnect = false; - AsyncResult.Connection = this; - // this SetAsCompleted will result in the callback to the - // activity happening in the WinRM thread - AsyncResult.SetAsCompleted(null); - RaiseEvents(Open); - RaiseEvents(Reconnect); - } - break; - - case RunspaceState.Broken: - { - // Dispose the broken runspace before retry - DisposeRunspace(); - - // connection attempt failed, retry - if (RetryCount > 0 && RetryAttempt < RetryCount) - { - RetryAttempt++; - - LogRetryAttempt(RetryAttempt, RetryCount); - - Timer timer = new Timer { AutoReset = false, Enabled = false, Interval = _retryInterval * 1000 }; - timer.Elapsed += RetryTimerElapsed; - timer.Start(); - } - else - { - // all retries have failed, end the asyncresult with an exception - // message - Busy = false; - - lock (_syncObject) - { - if (AsyncResult != null) - { - AsyncResult.Connection = null; - AsyncResult.SetAsCompleted(e.RunspaceStateInfo.Reason); - } - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "Disposing broken connection to {0}", _runspace.ConnectionInfo.ComputerName)); - - RaiseEvents(Open); - RaiseEvents(Disconnect); - RaiseEvents(Close); - RaiseEvents(Reconnect); - } - } - break; - - case RunspaceState.Disconnected: - { - ReadyForReconnect = true; - RaiseEvents(Disconnect); - } - break; - - case RunspaceState.Closed: - { - DisposeRunspace(); - RaiseEvents(Close); - } - break; - } - } - - private void LogRetryAttempt(uint retryAttempt, uint retryTotal) - { - RunCommandsArguments args = this.AsyncResult.AsyncState as RunCommandsArguments; - if (args != null) - { - PSActivityContext psActivityContext = args.PSActivityContext; - - // Write / Log that an activity retry was required. - if (psActivityContext.progress != null) - { - string progressActivity = ((System.Activities.Activity)psActivityContext.ActivityObject).DisplayName; - - if (string.IsNullOrEmpty(progressActivity)) - progressActivity = psActivityContext.ActivityType.Name; - - string retryMessage = String.Format(CultureInfo.CurrentCulture, Resources.RetryingConnection, retryAttempt, retryTotal); - - ProgressRecord progressRecord = new ProgressRecord(0, progressActivity, retryMessage); - lock (psActivityContext.progress) - { - psActivityContext.progress.Add(progressRecord); - } - } - } - } - - private void RaiseEvents(int eventType) - { - switch (eventType) - { - case Open: - { - if (OpenCompleted != null) OpenCompleted(this, EventArgs); - } - break; - case Close: - { - if (CloseCompleted != null) CloseCompleted(this, EventArgs); - } - break; - case Disconnect: - { - if (DisconnectCompleted != null) DisconnectCompleted(this, EventArgs); - } - break; - case Reconnect: - { - if (ReconnectCompleted != null) ReconnectCompleted(this, EventArgs); - } - break; - } - } - - private void DisposeRunspace() - { - _runspace.StateChanged -= RunspaceStateChanged; - _tracer.WriteMessage("PSW Conn: disposing runspace"); - _runspace.Dispose(); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingConnectionsDisposedCount); - } - - private void RetryTimerElapsed(object sender, ElapsedEventArgs elapsedEventArgs) - { - // specified retry interval timeout has elapsed, try again - // dispose the timer - Timer timer = sender as Timer; - if (timer != null) timer.Dispose(); - - // retry the connection again - OpenAsync(); - } - } - - #endregion Connection - - #region GetRunspaceAsyncResult - - /// - /// AsyncResult object returned by ConnectionManager - /// - /// the only reason this class has an internal scope is for - /// test purposes. Else this can be a private class inside - /// connection manager - internal class GetRunspaceAsyncResult : ConnectionAsyncResult - { - internal GetRunspaceAsyncResult(object state, AsyncCallback callback, Guid ownerId) : - base(state, callback, ownerId) - { - } - - internal Connection Connection { get; set; } - } - - #endregion GetRunspaceAsyncResult - - #region ThrottleOperation - - internal abstract class ThrottleOperation - { - internal event EventHandler OperationComplete; - private static readonly EventArgs EventArgs = new EventArgs(); - - internal void RaiseOperationComplete() - { - if (OperationComplete != null) OperationComplete(this, EventArgs); - } - - internal virtual void DoOperation() - { - throw new NotImplementedException(); - } - } - - internal class OpenOperation : ThrottleOperation - { - private readonly Connection _connection; - - internal OpenOperation(Connection connection) - { - _connection = connection; - _connection.OpenCompleted += HandleOpenCompleted; - } - - private void HandleOpenCompleted(object sender, EventArgs eventArgs) - { - _connection.OpenCompleted -= HandleOpenCompleted; - RaiseOperationComplete(); - } - - internal override void DoOperation() - { - _connection.OpenAsync(); - } - } - - internal class CloseOperation : ThrottleOperation - { - private readonly Connection _connection; - internal Connection Connection - { - get { return _connection; } - } - - internal CloseOperation(Connection connection) - { - _connection = connection; - _connection.CloseCompleted += HandleCloseCompleted; - } - - private void HandleCloseCompleted(object sender, EventArgs eventArgs) - { - _connection.CloseCompleted -= HandleCloseCompleted; - RaiseOperationComplete(); - } - - internal override void DoOperation() - { - _connection.CloseAsync(); - } - } - - internal class CloseOneAndOpenAnotherOperation : ThrottleOperation - { - private readonly Connection _connectionToClose; - private readonly Connection _connectionToOpen; - - internal CloseOneAndOpenAnotherOperation(Connection toClose, Connection toOpen) - { - _connectionToClose = toClose; - _connectionToOpen = toOpen; - - _connectionToClose.CloseCompleted += HandleCloseCompleted; - _connectionToOpen.OpenCompleted += HandleOpenCompleted; - } - - private void HandleOpenCompleted(object sender, EventArgs eventArgs) - { - _connectionToOpen.OpenCompleted -= HandleOpenCompleted; - RaiseOperationComplete(); - } - - private void HandleCloseCompleted(object sender, EventArgs eventArgs) - { - _connectionToClose.CloseCompleted -= HandleCloseCompleted; - _connectionToOpen.OpenAsync(); - } - - internal override void DoOperation() - { - _connectionToClose.CloseAsync(); - } - } - - internal class DisconnectOperation : ThrottleOperation - { - private readonly Connection _connection; - - internal DisconnectOperation(Connection connection) - { - _connection = connection; - _connection.DisconnectCompleted += HandleDisconnectCompleted; - } - - private void HandleDisconnectCompleted(object sender, EventArgs eventArgs) - { - _connection.DisconnectCompleted -= HandleDisconnectCompleted; - RaiseOperationComplete(); - } - - internal override void DoOperation() - { - _connection.DisconnectAsync(); - } - } - - internal class ReconnectOperation : ThrottleOperation - { - private readonly Connection _connection; - - internal ReconnectOperation(Connection connection) - { - _connection = connection; - _connection.ReconnectCompleted += HandleReconnectCompleted; - } - - private void HandleReconnectCompleted(object sender, EventArgs eventArgs) - { - _connection.ReconnectCompleted -= HandleReconnectCompleted; - RaiseOperationComplete(); - } - - internal override void DoOperation() - { - if (_connection.Runspace.RunspaceStateInfo.State != RunspaceState.Disconnected) - { - EventHandler HandleRunspaceStateChanged = null; - HandleRunspaceStateChanged = - delegate(object sender, RunspaceStateEventArgs e) - { - if (e.RunspaceStateInfo.State == RunspaceState.Disconnected) - { - _connection.ReconnectAsync(); - _connection.Runspace.StateChanged -= HandleRunspaceStateChanged; - } - }; - - _connection.Runspace.StateChanged += HandleRunspaceStateChanged; - - if (_connection.Runspace.RunspaceStateInfo.State == RunspaceState.Disconnected) - { - _connection.ReconnectAsync(); - _connection.Runspace.StateChanged -= HandleRunspaceStateChanged; - } - - return; - } - - _connection.ReconnectAsync(); - } - } - - #endregion ThrottleOperation - - internal class ConnectionManager : RunspaceProvider, IDisposable - { - #region RequestInfo - - private class RequestInfo - { - private WSManConnectionInfo _connectionInfo; - private uint _retryCount; - private GetRunspaceAsyncResult _asyncResult; - private uint _retryInterval; - - internal WSManConnectionInfo ConnectionInfo - { - get { return _connectionInfo; } - set { _connectionInfo = value; } - } - - internal uint RetryCount - { - get { return _retryCount; } - set { _retryCount = value; } - } - - internal GetRunspaceAsyncResult AsyncResult - { - get { return _asyncResult; } - set { _asyncResult = value; } - } - - internal uint RetryInterval - { - get { return _retryInterval; } - set { _retryInterval = value; } - } - } - - #endregion RequestInfo - - #region Private members - - // this is the connection pool indexed based on a given machine - // and a given session configuration - private readonly ConcurrentDictionary>> _connectionPool = - new ConcurrentDictionary>>(); - - // this is the list of computers to which a cleanup is requested - private readonly ConcurrentDictionary>> _cleanupComputers = - new ConcurrentDictionary>>(); - - private readonly int _idleTimeout; - private readonly int _maxOutBoundConnections; - private readonly int _maxConnectedSessions; - private readonly int _maxDisconnectedSessions; - - private int _isServicing; - private int _isServicingCallbacks; - private int _isServicingCleanups; - private const int Servicing = 1; - private const int NotServicing = 0; - private readonly Timer _timer; - private readonly ConcurrentQueue _inComingRequests = new ConcurrentQueue(); - - private readonly ConcurrentQueue _callbacks = - new ConcurrentQueue(); - - private int _timerFired; - private const int TimerFired = 1; - private const int TimerReset = 0; - private bool _isDisposed; - private bool _isDisposing; - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private readonly object _syncObject = new object(); - private static readonly PSPerfCountersMgr _perfCountersMgr = PSPerfCountersMgr.Instance; - - - /// - /// The servicing thread will release control of processing and the timer - /// thread will take control and process based on this wait handle - /// - private readonly ManualResetEvent _servicingThreadRelease = new ManualResetEvent(false); - - /// - /// The timer thread will release control of processing and the servicing thread - /// will take control and process based on this wait handle - /// - private readonly ManualResetEvent _timerThreadRelease = new ManualResetEvent(true); - - /// - /// This list is assumed to accessed by only the servicing thread and - /// hence is not designed to be thread safe - /// - private readonly List _pendingRequests = new List(); - - /// - /// number of sessions in the connected state - /// - /// this is made static in the interest of time - /// should not be static - //internal static int ConnectedSessionCount = 0; - private int _connectedSessionCount = 0; - - /// - /// number of sessions in the disconnected state - /// - /// this is made static in the interest of time - /// should not be static - //internal static int _disconnectedSessionCount = 0; - private int _disconnectedSessionCount = 0; - - private int _createdConnections = 0; - - /// - /// if we need to check whether runspaces need to be - /// disconnected - /// - private int _checkForDisconnect = 0; - private const int CheckForDisconnect = 1; - private const int DoNotCheckForDisconnect = 0; - - /// - /// Map of timers for each machine - /// - private readonly ConcurrentDictionary _timerMap = new ConcurrentDictionary(); - - #endregion Private Members - - #region Constructors - - internal ConnectionManager(int idleTimeout, int maxOutBoundConnections, int throttleLimit, int maxConnectedSessions, int maxDisconnectedSessions) - { - _idleTimeout = idleTimeout; - _maxOutBoundConnections = maxOutBoundConnections; - _throttleLimit = throttleLimit; - _maxConnectedSessions = maxConnectedSessions; - _maxDisconnectedSessions = maxDisconnectedSessions; - - _timer = new Timer {AutoReset = true, Interval = _idleTimeout, Enabled=false}; - _timer.Elapsed += HandleTimerElapsed; - _timer.Start(); - } - - #endregion Constructors - - #region Interface Methods - - /// - /// Get runspace for the specified connection info to be - /// used for running commands - /// - /// connection info to use - /// retry count - /// retry interval in ms - /// remote runspace to use - public override Runspace GetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval) - { - IAsyncResult asyncResult = BeginGetRunspace(connectionInfo, retryCount, retryInterval, null, null); - - return EndGetRunspace(asyncResult); - } - - /// - /// Begin for obtaining a runspace for the specified ConnectionInfo - /// - /// connection info to be used for remote connections - /// number of times to retry - /// optional user defined callback - /// optional user specified state - /// time in milliseconds before the next retry has to be attempted - /// async result - public override IAsyncResult BeginGetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval, AsyncCallback callback, object state) - { - GetRunspaceAsyncResult result = new GetRunspaceAsyncResult(state, callback, Guid.Empty); - - // Create a Request Object and queue the same - RequestInfo requestInfo = new RequestInfo - { - ConnectionInfo = connectionInfo, - RetryCount = retryCount, - AsyncResult = result, - RetryInterval = retryInterval - }; - - _tracer.WriteMessage("PSW ConnMgr: New incoming request for runspace queued"); - _inComingRequests.Enqueue(requestInfo); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingPendingRequestsQueueLength); - - // start servicing thread if required - CheckAndStartRequiredThreads(); - - return result; - } - - /// - /// End for obtaining a runspace for the specified connection info - /// - /// async result to end on - /// remote runspace to invoke commands on - public override Runspace EndGetRunspace(IAsyncResult asyncResult) - { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); - - GetRunspaceAsyncResult result = asyncResult as GetRunspaceAsyncResult; - - if (result == null) - throw new ArgumentException(Resources.InvalidAsyncResultSpecified, "asyncResult"); - - AssertNotDisposed(); - - // this will throw an exception when a runspace is not successfully - // available - result.EndInvoke(); - - Debug.Assert(result.Connection != null, "EndInvoke() should throw an exception if connection is null"); - - _tracer.WriteMessage("PSW ConnMgr: Request serviced and runspace returned"); - Runspace runspace = result.Connection != null ? result.Connection.Runspace : null; - - return runspace; - } - - /// - /// Release the runspace once the activity is done using the same - /// - /// runspace to release - public override void ReleaseRunspace(Runspace runspace) - { - AssertNotDisposed(); - - Connection connection = GetConnectionForRunspace(runspace); - _tracer.WriteMessage("PSW ConnMgr: Runspace released"); - - // at this point connection will be not null - // since GetConnectionForRunspace() should have - // handled all cases and raised an exception - Debug.Assert(connection != null, "GetConnectionForRunspace has not handled all cases and raised an exception"); - - connection.Busy = false; - connection.AsyncResult = null; - - CheckAndStartRequiredThreads(); - } - - /// - /// Request a cleanup to the destination specified in the - /// connection info. This means no runspaces will be held - /// to the specified connection info. - /// - /// connection info to which - /// cleanup is desired - ///callback to invoke - /// caller specified state - public override void RequestCleanup(WSManConnectionInfo connectionInfo, WaitCallback callback, object state) - { - if (_isDisposed || _isDisposing) - { - if (callback != null) - { - callback(state); - } - - return; - } - - // add this computer to the list of cleanup computers - string computerName = connectionInfo.ComputerName; - - ConcurrentQueue> callbacks = - _cleanupComputers.GetOrAdd(computerName, new ConcurrentQueue>()); - - var tuple = new Tuple(callback, state); - callbacks.Enqueue(tuple); - - CheckAndStartCleanupThread(); - } - - /// - /// Checks to see if the provider intentionally disconnected a runspace - /// or it went into disconnected state due to network issues - /// - /// runspace that needs to be checked - /// true - when intentionally disconnected - /// false - disconnected due to network issues - public override bool IsDisconnectedByRunspaceProvider(Runspace runspace) - { - Connection connection = GetConnectionForRunspace(runspace); - return connection.DisconnectedIntentionally; - } - - internal void DisconnectCalled() - { - lock (_syncObject) - { - _disconnectedSessionCount++; - _connectedSessionCount--; - } - } - internal void ReconnectCalled() - { - lock (_syncObject) - { - _connectedSessionCount++; - _disconnectedSessionCount--; - } - } - - #endregion Interface Methods - - #region Private Methods - - private static void ThrowInvalidRunspaceException(Runspace runspace) - { - throw new ArgumentException(Resources.InvalidRunspaceSpecified, "runspace"); - } - - /// - /// Handle the idle timer event - /// - /// unused - /// unused - private void HandleTimerElapsed(object sender, ElapsedEventArgs e) - { - // ensure that the servicing thread is done before proceeding - if (Interlocked.CompareExchange(ref _timerFired, TimerFired, TimerReset) == TimerFired) - { - _tracer.WriteMessage("PSW ConnMgr: Another timer thread is already servicing return"); - return; - } - - if (_isDisposed || _isDisposing) - { - return; - } - - try - { - _tracer.WriteMessage("PSW ConnMgr: Timer fired"); - - _servicingThreadRelease.WaitOne(); - _timerThreadRelease.Reset(); - - _tracer.WriteMessage("PSW ConnMgr: Timer servicing started"); - - // when the timer elapses mark all unused connections to be closed - Collection toRemoveComputer = new Collection(); - - foreach (string computername in _connectionPool.Keys) - { - ConcurrentDictionary> table = _connectionPool[computername]; - - Collection toRemoveConfig = new Collection(); - foreach (string configName in table.Keys) - { - ConcurrentDictionary connections = table[configName]; - Collection toRemoveConnections = new Collection(); - - lock (_syncObject) - { - foreach (Connection connection in connections.Values) - { - // if connection has been marked idle the last - // time remove resources - if (connection.Idle) - { - toRemoveConnections.Add(connection); - } - else if (!connection.Busy) - { - connection.Idle = true; - } - } - } - - // remove all connections that need to be removed - foreach (Connection connection in toRemoveConnections) - { - Connection removeConnection; - _createdConnections--; - // remove connection from the table before attempting to - // close it. This will ensure that this connection is not - // assigned by mistake - connections.TryRemove(connection.InstanceId, out removeConnection); - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "Closing idle connection to {0}", connection.Runspace.ConnectionInfo.ComputerName)); - SubmitOperation(new CloseOperation(connection)); - } - - // if there are no more entries in connections it needs - // to be removed from the table - if (connections.Count == 0) - { - toRemoveConfig.Add(configName); - } - - } // go through all configurations for a specified computer - - foreach (string configName in toRemoveConfig) - { - ConcurrentDictionary removedConnections; - table.TryRemove(configName, out removedConnections); - } - - // if there are no more entries in table it needs to be removed - // from the connection pool - if (table.Keys.Count == 0) - { - toRemoveComputer.Add(computername); - } - - } // go through all computernames - - // remove unwanted computernames from the connectionpool - foreach (string computerName in toRemoveComputer) - { - ConcurrentDictionary> removedTable; - _connectionPool.TryRemove(computerName, out removedTable); - } - - _tracer.WriteMessage("PSW ConnMgr: Timer servicing completed"); - - Interlocked.CompareExchange(ref _timerFired, TimerReset, TimerFired); - _timerThreadRelease.Set(); - } - catch (ObjectDisposedException) - { - // Ignoring this exception - } - finally - { - CheckAndStartRequiredThreads(); - } - } - - private void TraceThreadPoolInfo(string message) - { -#if DEBUG - int maxWorkerThreads, maxCompletionPortThreads, availableWorkerThreads, availableCompletionPortThreads, - minWorkerThreads, minCompletionPortThreads; - - ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads); - ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads); - ThreadPool.GetAvailableThreads(out availableWorkerThreads, out availableCompletionPortThreads); - - _tracer.WriteMessage(string.Format(System.Globalization.CultureInfo.InvariantCulture, - "PSW ConnMgr: {0} minwt:{1} mincpt:{2} wt:{3} ct:{4} awt:{5} act:{6}", - message, minWorkerThreads, minCompletionPortThreads, maxWorkerThreads, - maxCompletionPortThreads, availableWorkerThreads, - availableCompletionPortThreads)); -#else - _tracer.WriteMessage(string.Format(System.Globalization.CultureInfo.InvariantCulture, - "PSW ConnMgr: {0}", - message)); -#endif - } - - /// - /// Checks to see if a thread is already servicing and if not starts one - /// - private void CheckAndStartConnectionServicingThread() - { - if (Interlocked.CompareExchange(ref _isServicing, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("QueueUserWorkItem Connection Servicing thread"); - ThreadPool.QueueUserWorkItem(ServiceRequests); - } - - /// - /// Checks to see if a thread is already servicing callbacks - /// and if not starts one - /// - private void CheckAndStartCallbackServicingThread() - { - if (Interlocked.CompareExchange(ref _isServicingCallbacks, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("Callback thread"); - ThreadPool.QueueUserWorkItem(ServiceCallbacks); - } - - /// - /// Method that services the callbacks for all - /// successfully assigned runspaces - /// - /// unused - private void ServiceCallbacks(object state) - { - if (_isDisposed || _isDisposing) - { - return; - } - - GetRunspaceAsyncResult result; - while (_callbacks.TryDequeue(out result)) - { - result.SetAsCompleted(null); - } - - // release servicing callbacks - Interlocked.CompareExchange(ref _isServicingCallbacks, NotServicing, Servicing); - } - - internal void AddToPendingCallback(GetRunspaceAsyncResult asyncResult) - { - _callbacks.Enqueue(asyncResult); - CheckAndStartCallbackServicingThread(); - } - - private void CheckAndStartRequiredThreads() - { - if (_isDisposed || _isDisposing) - { - return; - } - - CheckAndStartConnectionServicingThread(); - CheckAndStartDisconnectReconnectThread(); - CheckAndStartThrottleManagerThread(); - CheckAndStartCleanupThread(); - } - - /// - /// Method that contains the core servicing logic - /// - /// not used - private void ServiceRequests(object state) - { - try - { - TraceThreadPoolInfo("Starting servicing thread"); - - // ensure that idle timer thread has completed execution - // before proceeding - _timerThreadRelease.WaitOne(); - _servicingThreadRelease.Reset(); - - if (NeedToReturnFromServicing()) - { - return; - } - - Collection toRemove = new Collection(); - - // service all pending requests - foreach (RequestInfo info in _pendingRequests) - { - if (ServiceOneRequest(info)) toRemove.Add(info); - - if (NeedToReturnFromServicing()) - { - return; - } - } - - // remove serviced requests from pending queues - foreach (RequestInfo info in toRemove) - { - _pendingRequests.Remove(info); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingForcedToWaitRequestsQueueLength, - -1); - - } - - // if timer has fired, return - if (NeedToReturnFromServicing()) - { - return; - } - - // start servicing the incoming requests - RequestInfo requestInfo; - while (_inComingRequests.TryDequeue(out requestInfo)) - { - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingPendingRequestsQueueLength, - -1); - if (!ServiceOneRequest(requestInfo)) - { - // the request could not be serviced now - // add it to the pending queue - _pendingRequests.Add(requestInfo); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingForcedToWaitRequestsQueueLength); - } - - if (NeedToReturnFromServicing()) - { - return; - } - - } // service incoming requests - } - catch (ObjectDisposedException) - { - // - } - finally - { - // set servicing as complete - SafelyReturnFromServicing(); - - // Try to start the servicing thread again if there are any _inComingRequests. - // This is required to fix the race condition between CheckAndStartRequiredThreads() and ServiceRequests() methods. - // - if (_inComingRequests.Count > 0) - { - CheckAndStartConnectionServicingThread(); - } - } - } - - /// - /// Services one single request - /// - /// request to service - /// true, if successfully serviced, false otherwise - private bool ServiceOneRequest(RequestInfo requestInfo) - { - string computerName = requestInfo.ConnectionInfo.ComputerName; - string configName = requestInfo.ConnectionInfo.ShellUri; - - // if a cleanup is requested for a specified computer - // do not service the request until it is good to do so - if (_cleanupComputers.ContainsKey(computerName)) - return false; - - int existingConnections = 0; - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount); - - // check if there is a table for this computername - ConcurrentDictionary> table = - _connectionPool.GetOrAdd(computerName, - new ConcurrentDictionary>()); - - // count the number of existing connections to this computer - existingConnections = table.Keys.Sum(key => table[key].Count); - - // check if there is a connection collection for the specified configuration - ConcurrentDictionary connections = table.GetOrAdd(configName, - new ConcurrentDictionary - ()); - - bool activityResumption = false; - RunCommandsArguments args = requestInfo.AsyncResult.AsyncState as RunCommandsArguments; - - if (args != null && args.ImplementationContext != null) - activityResumption = !args.ImplementationContext.DisconnectedRunspaceInstanceId.Equals(Guid.Empty); - - // Existing connections should not be used when remote activity is resuming as they have a valid remote runspace - // Remote activity resume operation has to reconnect to the disconnected runspace - if (!activityResumption) - { - // table is available, check if there are any free runspaces - // if the collections was newly created, then this loop will - // be skipped) - foreach (Connection connection in - connections.Values.Where(connection => !connection.Busy).Where(connection => ValidateConnection(requestInfo, connection))) - { - // the assumption is connected session count and - // disconnected session count apply only to running - // pipelines. So a connection that is not busy will - // not be in the disconnected state - //Debug.Assert(connection.Runspace.RunspaceStateInfo.State != RunspaceState.Disconnected, - // "A not Busy connection should not be in the disconnected state"); - _tracer.WriteMessage("PSW ConnMgr: Assigning existing connection to request"); - AssignConnection(requestInfo, connection); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return true; - } - } - - if (existingConnections < _maxOutBoundConnections) - { - _tracer.WriteMessage("PSW ConnMgr: Creating new connection to service request"); - // the number of connections hasn't maxed out for this - // computer - a new connection can be created - Connection connection = CreateConnection(requestInfo, connections); - - // when the open succeeds within the specified retry attempts - // the connection will be assigned. - // submit the connection to the pending operations queue instead - // of opening it directly - SubmitOperation(new OpenOperation(connection)); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return true; - } - - // when the existing connections are maxed out, there - // are one of two choices - // 1. all connections are busy, in which case - // the request cannot be serviced - // 2. there are some idle connections, which - // can be closed and a new one created - - Connection potentialConnection = null; - - // find the first available idle connection - ConcurrentDictionary removeConnections=null; - - foreach (var key in table.Keys) - { - removeConnections = table[key]; - - potentialConnection = removeConnections.Values.Where(connection => !connection.Busy).FirstOrDefault(); - - if (potentialConnection != null) break; - } - - if (potentialConnection == null) - { - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return false; /* case 1 */ - } - - // it is possible that the potential connection in fact - // is a good match, if so return the same - // Existing connections should not be used when remote activity is resuming as they have a valid remote runspace - // Remote activity resume operation has to reconnect to the disconnected runspace - if (!activityResumption && ValidateConnection(requestInfo, potentialConnection)) - { - _tracer.WriteMessage("PSW ConnMgr: Assigning potential connection to service request"); - AssignConnection(requestInfo, potentialConnection); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return true; - } - - // there is a potential connection which can be closed, - // close the same and create a new one - removeConnections.TryRemove(potentialConnection.InstanceId, out potentialConnection); - - Debug.Assert(potentialConnection != null, "Trying to remove an element not in the dictionary"); - _tracer.WriteMessage("PSW ConnMgr: Closing potential connection and creating a new one to service request"); - - // Create the connection object which will be returned after - // the potential connection is closed - _createdConnections--; - Connection newConnection = CreateConnection(requestInfo, connections); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingConnectionsClosedReopenedCount); - SubmitOperation(new CloseOneAndOpenAnotherOperation(potentialConnection, newConnection)); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, - -1); - return true; - } - - private Connection CreateConnection(RequestInfo requestInfo, ConcurrentDictionary connections) - { - Connection connection = new Connection(this) - { - ConnectionInfo = requestInfo.ConnectionInfo, - RetryCount = requestInfo.RetryCount, - RetryInterval = requestInfo.RetryInterval, - RetryAttempt = 0, - AsyncResult = requestInfo.AsyncResult, - Busy = true - }; - - // the busy status should be set before adding to the - // collection so that this connection is accounted in - // the total and is not assigned to another request - connections.TryAdd(connection.InstanceId, connection); - - _createdConnections++; - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.PSRemotingConnectionsCreatedCount); - return connection; - } - - /// - /// Do everything required so that servicing thread can return - /// - private void SafelyReturnFromServicing() - { - if (_isDisposed || _isDisposing) - { - return; - } - - CheckAndStartRequiredThreads(); - - try - { - _tracer.WriteMessage("PSW ConnMgr: Safely returning from servicing"); - Interlocked.CompareExchange(ref _isServicing, NotServicing, Servicing); - _servicingThreadRelease.Set(); - } - catch (ObjectDisposedException) - { - // Ignoring ObjectDisposedException - } - } - - /// - /// Check if the servicing thread need to stop processing requests - /// - /// true if processing needs to stop - private bool NeedToReturnFromServicing() - { - if(_isDisposed || _isDisposing) - { - _tracer.WriteMessage("PSW ConnMgr: Returning from servicing as ConnMgr is disposed or being disposed"); - return true; - } - - // servicing thread has to return if the timer - // has been fired - if (_timerFired == TimerFired) - { - _tracer.WriteMessage("PSW ConnMgr: Returning from servicing since timer fired"); - return true; - } - - if (_createdConnections >= _maxConnectedSessions) - { - _tracer.WriteMessage("PSW ConnMgr: Setting check for runspaces disconnect flag"); - Interlocked.CompareExchange(ref _checkForDisconnect, CheckForDisconnect, DoNotCheckForDisconnect); - } - - return false; - } - - private static bool ValidateConnection(RequestInfo requestInfo, Connection connection) - { - if (connection.Runspace.RunspaceStateInfo.State != RunspaceState.Opened ) return false; - - WSManConnectionInfo connectionInfo = requestInfo.ConnectionInfo; - - // when validation is called the Connection will have a runspace populated - WSManConnectionInfo connectionInfo2 = connection.Runspace.OriginalConnectionInfo as WSManConnectionInfo; - - // Runspace.OriginalConnectionInfo is null for disconnected runspace after process crash - if(connectionInfo2 == null) - connectionInfo2 = connection.Runspace.ConnectionInfo as WSManConnectionInfo; - - if (connectionInfo2 == null) return false; - - // check URI related stuff - if (!WorkflowUtils.CompareConnectionUri(connectionInfo, connectionInfo2)) - { - return false; - } - - // compare shell URI - if (!WorkflowUtils.CompareShellUri(connectionInfo.ShellUri, connectionInfo2.ShellUri)) - { - return false; - } - - // check authentication - if (!WorkflowUtils.CompareAuthentication(connectionInfo.AuthenticationMechanism, connectionInfo2.AuthenticationMechanism)) - { - return false; - } - - // check credentials if present - if(!WorkflowUtils.CompareCredential(connectionInfo.Credential, connectionInfo2.Credential)) - { - return false; - } - - //check certificate thumbprint - if (!WorkflowUtils.CompareCertificateThumbprint(connectionInfo.CertificateThumbprint, connectionInfo2.CertificateThumbprint)) - { - return false; - } - - //check proxy settings - if (!WorkflowUtils.CompareProxySettings(connectionInfo, connectionInfo2)) - { - return false; - } - - //check rest of wsman settings - if (!WorkflowUtils.CompareOtherWSManSettings(connectionInfo, connectionInfo2)) - { - return false; - } - - // check open timeout - if (connectionInfo2.IdleTimeout < connectionInfo.IdleTimeout) - return false; - - return true; - } - - /// - /// Service the request using the available connection - /// - /// request to service - /// connection to use for servicing - private void AssignConnection(RequestInfo requestInfo, Connection connection) - { - IAsyncResult asyncResult = requestInfo.AsyncResult; - GetRunspaceAsyncResult result = asyncResult as GetRunspaceAsyncResult; - Debug.Assert(result != null, "IAsyncResult should be GetRunspaceAsyncResult"); - - connection.Busy = true; - - connection.AsyncResult = result; - result.Connection = connection; - - AddToPendingCallback(result); - } - - /// - /// Find the connection object given a runspace - /// - /// runspace whose connection - /// needs to be found - /// Connection if found, null otherwise - private Connection GetConnectionForRunspace(Runspace runspace) - { - if (runspace == null) - throw new ArgumentNullException("runspace"); - - // OriginalConnectionInfo will be null for reconnected remoterunspace after crash or shutdown - WSManConnectionInfo connectionInfo = runspace.ConnectionInfo as WSManConnectionInfo; - - if (connectionInfo == null) - { - _tracer.WriteMessage("PSW ConnMgr: Incoming connectioninfo is null"); - // throw an exception here - ThrowInvalidRunspaceException(runspace); - } - - string computername = connectionInfo.ComputerName; - string configname = connectionInfo.ShellUri; - - ConcurrentDictionary> table; - ConcurrentDictionary connections; - - if (!_connectionPool.TryGetValue(computername, out table)) - { - // invalid runspace specified, throw an exception here - _tracer.WriteMessage("PSW ConnMgr: Cannot find table for computername " + computername); - ThrowInvalidRunspaceException(runspace); - } - - if (!table.TryGetValue(configname, out connections)) - { - // invalid runspace specified, throw an exception here - _tracer.WriteMessage("PSW ConnMgr: Cannot find list for config " + configname); - ThrowInvalidRunspaceException(runspace); - } - - foreach (Connection connection in - connections.Values.Where(connection => connection.Runspace != null).Where(connection => connection.Runspace.InstanceId == runspace.InstanceId)) - { - return connection; - } - - // if this point is reached, then there is no match - // invalid runspace specified, throw an exception - _tracer.WriteMessage("PSW ConnMgr: Cannot find the actual connection object"); - ThrowInvalidRunspaceException(runspace); - - return null; - } - - /// - /// Check and start a method which will do cleanups to - /// the specified computer - /// - private void CheckAndStartCleanupThread() - { - if (Interlocked.CompareExchange(ref _isServicingCleanups, Servicing, NotServicing) != NotServicing) - return; - - TraceThreadPoolInfo("Cleanup thread"); - - ThreadPool.QueueUserWorkItem(ServiceCleanupRequests); - } - - /// - /// Worker method for servicing cleanups to requested computers - /// - /// - private void ServiceCleanupRequests(object state) - { - - foreach (string computerName in _cleanupComputers.Keys) - { - ConcurrentDictionary> table; - _connectionPool.TryGetValue(computerName, out table); - - if (table == null) - { - RaiseCallbacksAfterCleanup(computerName); - continue; - } - - foreach (string configName in table.Keys) - { - ConcurrentDictionary connections; - table.TryGetValue(configName, out connections); - - if (connections == null) continue; - lock (_syncObject) - { - foreach (CloseOperation closeOperation in - connections.Values.Where(connection => !connection.Busy).Select(connection => new CloseOperation(connection))) - { - closeOperation.OperationComplete += HandleCloseOperationComplete; - SubmitOperation(closeOperation); - } - } - } - } - - Interlocked.CompareExchange(ref _isServicingCleanups, NotServicing, Servicing); - } - - /// - /// Handles OperationComplete of close operations i.e - /// when connections to a specified computer is closed - /// - /// the CloseOperation object - /// that initiated this event - /// event parameters - private void HandleCloseOperationComplete(object sender, EventArgs e) - { - // the specified connection was successfully closed - CloseOperation closeOperation = sender as CloseOperation; - Debug.Assert(closeOperation != null, "CloseOperation object need to be returned in OperationComplete event"); - closeOperation.OperationComplete -= HandleCloseOperationComplete; - - Connection connection = closeOperation.Connection; - WSManConnectionInfo connectionInfo = connection.Runspace.ConnectionInfo as WSManConnectionInfo; - if (connectionInfo == null) return; - - string computerName = connectionInfo.ComputerName; - string configName = connectionInfo.ShellUri; - - ConcurrentDictionary> table; - _connectionPool.TryGetValue(computerName, out table); - if (table == null) return; - - ConcurrentDictionary connections; - table.TryGetValue(configName, out connections); - if (connections == null) return; - - // remove the connection from the table for this configName - Connection removedConnection; - connections.TryRemove(connection.InstanceId, out removedConnection); - if (connections.Count != 0) return; - - // if there are no more connections to this configName - // remove the table for the configName - ConcurrentDictionary removedConnections; - table.TryRemove(configName, out removedConnections); - if (table.Count != 0) return; - - // if there are no more tables for any specified configNames - // for this computerName, remove the table for the computerName - ConcurrentDictionary> removedTable; - _connectionPool.TryRemove(computerName, out removedTable); - - if (removedTable != null) - { - // if this thread is the one that removed the table, then - // make all the callbacks in this thread - RaiseCallbacksAfterCleanup(computerName); - } - } - - /// - /// Raise all the callbacks to the specified computer - /// after the requested cleanup - /// - /// computer to which the - /// callback needs to be raised - private void RaiseCallbacksAfterCleanup(string computerName) - { - bool alreadyAdded = _timerMap.Values.Contains(computerName, StringComparer.OrdinalIgnoreCase); - - // before making the callbacks fire the timer - Timer timer = new Timer(); - _timerMap.TryAdd(timer, computerName); - - timer.Elapsed += HandleCleanupWaitTimerElapsed; - - ConcurrentQueue> waitCallbacks; - if (!_cleanupComputers.TryGetValue(computerName, out waitCallbacks)) - { - _tracer.WriteMessage("PSW ConnMgr: Cannot find specified computer in _waitCallbacks dictionary: " + computerName); - return; - } - - Tuple tuple; - Collection> callbacks = new Collection>(); - - int highest = 0; - bool calledFromActivity = false; - - while (waitCallbacks.TryDequeue(out tuple)) - { - if (tuple.Item2 != null) - { - RunCommandsArguments args = tuple.Item2 as RunCommandsArguments; - - if (args != null) - { - if (highest < args.CleanupTimeout) - { - highest = args.CleanupTimeout; - } - - calledFromActivity = true; - } - } - - callbacks.Add(tuple); - } - - if (highest != 0) - { - timer.Interval = highest * 1000; - timer.AutoReset = false; - timer.Enabled = true; - - foreach (Tuple t in callbacks) - { - if (t.Item1 != null) - t.Item1(t.Item2); - } - } - else - { - if (!alreadyAdded) - { - HandleCleanupWaitTimerElapsed(timer, null); - } - else - { - string unused; - _timerMap.TryRemove(timer, out unused); - } - } - - if (!calledFromActivity) - { - foreach(var callbackTuple in callbacks) - { - if (callbackTuple.Item1 != null) - callbackTuple.Item1(callbackTuple.Item2); - } - } - } - - /// - /// Timer elapsed handler for the specified computer. Once - /// the timer elapses, new connections to the specified - /// computer will be allowed - /// - /// timer that generated the event - /// event arguments - private void HandleCleanupWaitTimerElapsed(object sender, ElapsedEventArgs e) - { - Timer timer = sender as Timer; - Debug.Assert(timer != null, "Sender cannot be null"); - - string computerName; - _timerMap.TryGetValue(timer, out computerName); - if (!string.IsNullOrEmpty(computerName)) - { - ConcurrentQueue> removedCallbacks; - _cleanupComputers.TryRemove(computerName, out removedCallbacks); - } - - // remove entry from the timer map and do cleanup - _timerMap.TryRemove(timer, out computerName); - - timer.Elapsed -= HandleCleanupWaitTimerElapsed; - timer.Dispose(); - - CheckAndStartRequiredThreads(); - } - - #endregion Private Methods - - #region Test Helper Methods - - internal bool IsConnectionPoolEmpty() - { - return _connectionPool.Keys.Count > 0; - } - - internal IEnumerable GetConnectionEnumerator() - { - return new ConnectionEnumerator(_connectionPool); - } - #endregion Test Helper Methods - - #region Dispose - - /// - /// Dispose the connection manager - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose the connection manager - /// - /// - protected void Dispose(bool isDisposing) - { - if (_isDisposed || !isDisposing) - return; - - lock (_syncObject) - { - if (_isDisposed) - return; - - _isDisposing = true; - - // set these values to servicing so that a thread is - // not started - Interlocked.CompareExchange(ref _isServicing, Servicing, NotServicing); - Interlocked.CompareExchange(ref _isServicingCallbacks, Servicing, NotServicing); - Interlocked.CompareExchange(ref _isServicingCleanups, Servicing, NotServicing); - Interlocked.CompareExchange(ref _timerFired, TimerFired, TimerReset); - - // close and clear all connections in connection pool - ClearAll(); - - // This should be done after closing all connections. - // Throttling thread executes the close operations - Interlocked.CompareExchange(ref _isOperationsServiced, Servicing, NotServicing); - - _timer.Elapsed -= HandleTimerElapsed; - _timer.Dispose(); - - var objectDisposedException = new ObjectDisposedException("ConnectionManager"); - GetRunspaceAsyncResult item; - while (_callbacks.TryDequeue(out item)) - { - item.SetAsCompleted(objectDisposedException); - } - _cleanupComputers.Clear(); - - RequestInfo info; - while (_inComingRequests.Count > 0) - { - _inComingRequests.TryDequeue(out info); - info.AsyncResult.SetAsCompleted(objectDisposedException); - } - - foreach(var reqInfo in _pendingRequests) - { - reqInfo.AsyncResult.SetAsCompleted(objectDisposedException); - } - _pendingRequests.Clear(); - - ThrottleOperation operation; - while (_pendingQueue.Count > 0) - { - _pendingQueue.TryDequeue(out operation); - } - - _timerMap.Clear(); - _servicingThreadRelease.Close(); - _timerThreadRelease.Close(); - _testHelperCloseDone.Close(); - _tracer.Dispose(); - - _isDisposed = true; - } - } - - private void AssertNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("ConnectionManager"); - } - } - #endregion Dispose - - #region Throttling - - /// - /// queue of operations to be used for throttling - /// - private readonly ConcurrentQueue _pendingQueue = new ConcurrentQueue(); - - /// - /// Count of operations in progress - /// - private int _inProgressCount = 0; - - /// - /// throttle limit - includes open/close/connect/disconnect operations - /// - private readonly int _throttleLimit; - - /// - /// is the queue of operations being serviced - /// - private int _isOperationsServiced; - - /// - /// submit an operation to the queue - /// - /// operation to submit - private void SubmitOperation(ThrottleOperation operation) - { - _pendingQueue.Enqueue(operation); - CheckAndStartThrottleManagerThread(); - } - - private void CheckAndStartThrottleManagerThread() - { - // if not already set, set flag that pending operations queue is being serviced - // else another thread is already servicing so return - if (Interlocked.CompareExchange(ref _isOperationsServiced, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("Queuing user workitem Running operations in throttle queue"); - - ThreadPool.QueueUserWorkItem(StartOperationsFromQueue); - } - - /// - /// Start operations upto throttle limit from the queue - /// - /// this method is thread safe. It starts all pending - /// operations in the first calling thread. The assumption here - /// is that starting a few operations isn't expensive and so - /// the calling thread is not blocked for a long period of time - private void StartOperationsFromQueue(object state) - { - if (_isDisposed) - { - return; - } - - TraceThreadPoolInfo("Running operations in throttle queue"); - - // at this point, there will be only one thread which will be - // servicing operations from the pending operations queue - ThrottleOperation operation; - - while (_inProgressCount < _throttleLimit && _pendingQueue.TryDequeue(out operation)) - { - operation.OperationComplete += HandleOperationComplete; - Interlocked.Increment(ref _inProgressCount); - operation.DoOperation(); - } - - _tracer.WriteMessage("PSW ConnMgr: Done throttling"); - // set flag that pending operations queue is not being serviced - Interlocked.CompareExchange(ref _isOperationsServiced, NotServicing, Servicing); - } - - private void HandleOperationComplete (Object sender, EventArgs e) - { - ThrottleOperation operation = sender as ThrottleOperation; - Debug.Assert(operation != null, "OperationComplete event does not pass ThrottleOperation as sender"); - operation.OperationComplete -= HandleOperationComplete; - Interlocked.Decrement(ref _inProgressCount); - CheckAndStartRequiredThreads(); - } - - #endregion Throttling - - #region Connection Enumerator - - private class ConnectionEnumerator : IEnumerator, IEnumerable - { - private Connection _currentConnection; - private ConcurrentDictionary _currentConnections; - private ConcurrentDictionary> _currentTable; - private readonly IEnumerator _tableEnumerator; - private IEnumerator _configEnumerator; - private IEnumerator _connectionEnumerator; - - private readonly ConcurrentDictionary>> - _connectionPool; - - internal ConnectionEnumerator(ConcurrentDictionary>> connectionPool) - { - Debug.Assert(connectionPool != null, "ConnectionPool cannot be null"); - _connectionPool = connectionPool; - _tableEnumerator = _connectionPool.Keys.GetEnumerator(); - } - - /// - /// - /// - /// - public bool MoveNext() - { - while (true) - { - if (_connectionEnumerator != null && _connectionEnumerator.MoveNext()) - { - Guid key = (Guid) _connectionEnumerator.Current; - _currentConnection = null; - _currentConnections.TryGetValue(key, out _currentConnection); - if (_currentConnection != null) - return true; - else - continue; - } - - if (_configEnumerator != null && _configEnumerator.MoveNext()) - { - string configName = (string)_configEnumerator.Current; - _currentTable.TryGetValue(configName, out _currentConnections); - _connectionEnumerator = _currentConnections.Keys.GetEnumerator(); - - Debug.Assert(_connectionEnumerator != null, "Enumerator should not be null"); - - continue; - } - - if (_tableEnumerator.MoveNext()) - { - string tableName = (string)_tableEnumerator.Current; - _connectionPool.TryGetValue(tableName, out _currentTable); - _configEnumerator = _currentTable.Keys.GetEnumerator(); - Debug.Assert(_configEnumerator != null, "Enumerator should not be null"); - continue; - } - - break; - } - - return false; - } - - /// - /// - /// - public void Reset() - { - _tableEnumerator.Reset(); - _currentTable = (ConcurrentDictionary>)_tableEnumerator.Current; - _configEnumerator = _currentTable.Keys.GetEnumerator(); - _currentConnections = (ConcurrentDictionary)_configEnumerator.Current; - _connectionEnumerator = _currentConnections.Keys.GetEnumerator(); - Guid key = (Guid)_connectionEnumerator.Current; - _currentConnections.TryGetValue(key, out _currentConnection); - } - - /// - /// - /// - public object Current - { - get { return _currentConnection; } - } - - /// - /// - /// - /// - public IEnumerator GetEnumerator() - { - return this; - } - } - - #endregion Connection Enumerator - - #region Disconnect/Reconnect - - private int _isReconnectServicing; - private const long NotMarked = 0; - private const long Marked = 1; - private long _newConnectionMarked = NotMarked; - - private void CheckAndStartDisconnectReconnectThread() - { - if (_checkForDisconnect != CheckForDisconnect) return; - - if (Interlocked.CompareExchange(ref _isReconnectServicing, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("Queuing user workitem disconnect/reconnect worker"); - ThreadPool.QueueUserWorkItem(DisconnectReconnectWorker); - } - - /// - /// Disconnect and reconnect to different runspaces as necessary - /// so as to help the connection manager scale and run a large - /// number of commands on a large number of remote nodes - /// - /// unused - private void DisconnectReconnectWorker(object state) - { - if (_isDisposed || _isDisposing) - { - return; - } - - TraceThreadPoolInfo("Running disconnect/reconnect worker"); - - Interlocked.CompareExchange(ref _newConnectionMarked, NotMarked, Marked); - - while (Interlocked.CompareExchange(ref _newConnectionMarked, Marked, NotMarked) == Marked) - { - foreach (Connection connection in - (new ConnectionEnumerator(_connectionPool))) - { - if (_disconnectedSessionCount > _maxDisconnectedSessions) break; - - if (!connection.Busy) continue; - if (!connection.ReadyForDisconnect) continue; - - connection.DisconnectedIntentionally = true; - // add a disconnect operation to the queue of operations - SubmitOperation(new DisconnectOperation(connection)); - } - - // at this point all relevant connections have been disconnected - // now connect to every remote connection for a specified period of time - - // only connections which were initially marked for disconnected - // would have been disconnected - foreach (Connection connection in - (new ConnectionEnumerator(_connectionPool))) - { - if (_connectedSessionCount > _maxConnectedSessions) break; - - if (!connection.ReadyForReconnect) continue; - - connection.DisconnectedIntentionally = false; - SubmitOperation(new ReconnectOperation(connection)); - } - } - - _tracer.WriteMessage("PSW ConnMgr: Exiting disconnect reconnect worker"); - // reset flag that disconnect/reconnect thread is running - Interlocked.CompareExchange(ref _isReconnectServicing, NotServicing, Servicing); - } - - /// - /// Callback to indicate that this runspace been initiated with - /// a pipeline and can be disconnected - /// - /// runspace that needs to be marked as - /// ready for disconnect - public override void ReadyForDisconnect(Runspace runspace) - { - Connection connection = GetConnectionForRunspace(runspace); - _tracer.WriteMessage("PSW ConnMgr: Runspace marked as ready for disconnect"); - - // at this point connection will be not null - // since GetConnectionForRunspace() should have - // handled all cases and raised an exception - Debug.Assert(connection != null, "GetConnectionForRunspace has not handled all cases and raised an exception"); - - if (connection.Busy) - { - // mark the connection as ready for disconnect - // the thread which is servicing disconnect and reconnect - // will take care of disconnecting and reconnecting the same - lock (_syncObject) - { - if (!connection.Busy) - return; - - connection.ReadyForDisconnect = true; - Interlocked.CompareExchange(ref _newConnectionMarked, NotMarked, Marked); - } - - CheckAndStartRequiredThreads(); - } - } - - #endregion Disconnect/Reconnect - - #region Test Helpers - - private readonly ManualResetEvent _testHelperCloseDone = new ManualResetEvent(false); - internal void ClearAll() - { - if (_connectionPool.Count > 0) - { - _testHelperCloseDone.Reset(); - - foreach (CloseOperation operation in - from Connection connection in new ConnectionEnumerator(_connectionPool) - select new CloseOperation(connection)) - { - operation.OperationComplete += OperationComplete; - SubmitOperation(operation); - } - - _testHelperCloseDone.WaitOne(); - - _connectionPool.Clear(); - } - } - - private void OperationComplete(object sender, EventArgs e) - { - CloseOperation closeOperation = sender as CloseOperation; - Debug.Assert(closeOperation != null, "CloseOperation not returned as sender"); - closeOperation.OperationComplete -= OperationComplete; - - if (_pendingQueue.Count == 0) - { - _testHelperCloseDone.Set(); - } - } - - #endregion Test Helpers - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/Constants.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/Constants.cs deleted file mode 100644 index 2d178f83bbe..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/Constants.cs +++ /dev/null @@ -1,91 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Reflection; -using System.Collections.Generic; -using System.Globalization; - -namespace Microsoft.PowerShell.Workflow -{ - internal static class Constants - { - - internal const string AsJob = "AsJob"; - - // Remoting-related constants - internal const string ComputerName = "PSComputerName"; - internal const string Credential = "PSCredential"; - internal const string Port = "PSPort"; - internal const string UseSSL = "PSUseSSL"; - internal const string ConfigurationName = "PSConfigurationName"; - internal const string ApplicationName = "PSApplicationName"; - internal const string ConnectionURI = "PSConnectionURI"; - internal const string AllowRedirection = "PSAllowRedirection"; - internal const string SessionOption = "PSSessionOption"; - internal const string Authentication = "PSAuthentication"; - internal const string AuthenticationLevel = "PSAuthenticationLevel"; - internal const string CertificateThumbprint = "PSCertificateThumbprint"; - internal const string PSParameterCollection = "PSParameterCollection"; - internal const string PSInputCollection = "PSInputCollection"; - internal const string InputObject = "InputObject"; - internal const string PSSenderInfo = "PSSenderInfo"; - internal const string PSCurrentDirectory = "PSCurrentDirectory"; - internal const string PSSuspendOnError = "PSSuspendOnError"; - - // PowerShell-common constants - internal const string Verbose = "Verbose"; - internal const string Debug = "Debug"; - internal const string ErrorAction = "ErrorAction"; - internal const string WarningAction = "WarningAction"; - internal const string InformationAction = "InformationAction"; - internal const string ErrorVariable = "ErrorVariable"; - internal const string WarningVariable = "WarningVariable"; - internal const string InformationVariable = "InformationVariable"; - internal const string OutVariable = "OutVariable"; - internal const string OutBuffer = "OutBuffer"; - internal const string PipelineVariable = "PipelineVariable"; - - // Retry policy constants - internal const string ConnectionRetryCount = "PSConnectionRetryCount"; - internal const string ActionRetryCount = "PSActionRetryCount"; - internal const string ConnectionRetryIntervalSec = "PSConnectionRetryIntervalSec"; - internal const string ActionRetryIntervalSec = "PSActionRetryIntervalSec"; - - internal const string PrivateMetadata = "PSPrivateMetadata"; - internal const string WorkflowTakesPrivateMetadata = "WorkflowTakesPrivateMetadata"; - internal const string WorkflowJobCreationContext = "WorkflowJobCreationContext"; - - // Timers - internal const string PSRunningTime = "PSRunningTimeoutSec"; - internal const string PSElapsedTime = "PSElapsedTimeoutSec"; - internal const string Int32MaxValueDivideByThousand = "2147483"; - - internal const string ModulePath = "PSWorkflowRoot"; - internal const string JobName = "JobName"; - internal const string DefaultComputerName = "localhost"; - - // Job Metadata constants - internal const string JobMetadataInstanceId = "InstanceId"; - internal const string JobMetadataSessionId = "Id"; - internal const string JobMetadataName = "Name"; - internal const string JobMetadataCommand = "Command"; - internal const string JobMetadataStateReason = "Reason"; - internal const string JobMetadataParentName = "ParentName"; - internal const string JobMetadataParentCommand = "ParentCommand"; - internal const string JobMetadataParentInstanceId = "ParentInstanceId"; - internal const string JobMetadataParentSessionId = "ParentSessionId"; - internal const string JobMetadataLocation = "Location"; - internal const string JobMetadataStatusMessage = "StatusMessage"; - internal const string JobMetadataUserName = "UserName"; - internal const string JobMetadataPid = "ProcessId"; - internal const string JobMetadataFilterState = "State"; - - internal const string Persist = "PSPersist"; - internal const string PSRequiredModules = "PSRequiredModules"; - - internal const string PSWorkflowErrorAction = "PSWorkflowErrorAction"; - internal const int MaxAllowedPersistencePathLength = 120; - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefaultWorkflowSessionConfiguration.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefaultWorkflowSessionConfiguration.cs deleted file mode 100644 index e5bbab4a0cb..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefaultWorkflowSessionConfiguration.cs +++ /dev/null @@ -1,126 +0,0 @@ -/********************************************************************++ - * Copyright (c) Microsoft Corporation. All rights reserved. - * --********************************************************************/ - -using System.Collections.ObjectModel; -using System.IO; -using System.Management.Automation.Remoting; -using System; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.Management.Automation.Tracing; -using System.Globalization; -using Microsoft.PowerShell.Commands; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// Configuration for the M3P endpoint - /// - public class PSWorkflowSessionConfiguration : PSSessionConfiguration - { - /// - /// IsWorkflowTypeEndpoint - /// - internal static bool IsWorkflowTypeEndpoint; - - #region Overrides of PSSessionConfiguration - - /// - /// - /// - /// - /// - /// - public override InitialSessionState GetInitialSessionState(PSSenderInfo senderInfo) - { - throw new NotImplementedException(); - } - - /// - /// - /// - /// - /// - /// - /// - public override InitialSessionState GetInitialSessionState(PSSessionConfigurationData sessionConfigurationData, PSSenderInfo senderInfo, string configProviderId) - { - Tracer structuredTracer = new Tracer(); - - structuredTracer.Correlate(); - - if (sessionConfigurationData == null) - throw new ArgumentNullException("sessionConfigurationData"); - - if (senderInfo == null) - throw new ArgumentNullException("senderInfo"); - - if (string.IsNullOrEmpty(configProviderId)) - throw new ArgumentNullException("configProviderId"); - - if (Interlocked.CompareExchange(ref _modulesLoaded, ModulesLoaded, ModulesNotLoaded) == ModulesNotLoaded) - { - // it is sufficient if Populate() is called the first time and - // modules are loaded once - - try - { - IsWorkflowTypeEndpoint = true; - - PSWorkflowConfigurationProvider workflowConfiguration = WorkflowJobSourceAdapter.GetInstance().GetPSWorkflowRuntime().Configuration; - if (workflowConfiguration == null) - throw new InvalidOperationException("PSWorkflowConfigurationProvider is null"); - - workflowConfiguration.Populate(sessionConfigurationData.PrivateData, configProviderId, senderInfo); - - // now get all the modules in the specified path and import the same - if (sessionConfigurationData.ModulesToImport != null) - { - foreach (var module in sessionConfigurationData.ModulesToImport) - { - ModuleSpecification moduleSpec = null; - if (ModuleSpecification.TryParse(module, out moduleSpec)) - { - var modulesToImport = new Collection { moduleSpec }; - InitialSessionState.ImportPSModule(modulesToImport); - } - else - { - InitialSessionState.ImportPSModule(new[] { Environment.ExpandEnvironmentVariables(module) }); - } - } - } - - // Start the workflow job manager, if not started, to add an event handler for zero active sessions changed events - // This is required to auto shutdown the workflow type shared process when no workflow jobs have scheduled/inprogress and when no active sessions - WorkflowJobSourceAdapter.GetInstance().GetJobManager(); - } - catch(Exception) - { - // if there is an exception in either Populate() or Importing modules - // we consider that it is not loaded - Interlocked.CompareExchange(ref _modulesLoaded, ModulesNotLoaded, ModulesLoaded); - throw; - } - } - - if (configProviderId.ToLower(CultureInfo.InvariantCulture).Equals("http://schemas.microsoft.com/powershell/microsoft.windows.servermanagerworkflows")) - { - PSSessionConfigurationData.IsServerManager = true; - } - - return InitialSessionState; - } - - private static readonly InitialSessionState InitialSessionState = - InitialSessionState.CreateRestricted(SessionCapabilities.WorkflowServer | SessionCapabilities.RemoteServer | SessionCapabilities.Language); - - private static int _modulesLoaded = ModulesNotLoaded; - private const int ModulesNotLoaded = 0; - private const int ModulesLoaded = 1; - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefinitionCache.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefinitionCache.cs deleted file mode 100644 index 4bdcbc67c71..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/DefinitionCache.cs +++ /dev/null @@ -1,608 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Tracing; -using Microsoft.PowerShell.Commands; -using System.Reflection; - -namespace Microsoft.PowerShell.Workflow -{ - internal sealed class DefinitionCache - { - - #region Privates - - internal struct WorkflowDetails - { - internal Activity ActivityTree; - internal string CompiledAssemblyPath; - internal string CompiledAssemblyName; - internal bool IsWindowsActivity; - }; - - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// Cache of workflow details. This is unbounded - /// - private readonly Dictionary _workflowDetailsCache = - new Dictionary(new CompareBasedOnInstanceId()); - - /// - /// this is the cache of compiled activities which will be bounded - /// - /// this is separate since Opalis will not use our cache - private readonly ConcurrentDictionary _cachedActivities = - new ConcurrentDictionary(new CompareBasedOnInstanceId()); - - private System.Timers.Timer _cleanupTimer; - private int activitiesCleanupIntervalMSec; - - private static readonly DefinitionCache _instance = new DefinitionCache(); - - private DefinitionCache() - { - activitiesCleanupIntervalMSec = WorkflowJobSourceAdapter.GetInstance().GetPSWorkflowRuntime().Configuration.ActivitiesCacheCleanupIntervalMSec; - - _cleanupTimer = new System.Timers.Timer(activitiesCleanupIntervalMSec); - _cleanupTimer.Elapsed += HandleCleanupTimerElapsed; - _cleanupTimer.AutoReset = false; - _cleanupTimer.Start(); - } - - private void HandleCleanupTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) - { - List activitiesToRemove = new List(); - Activity activity; - - foreach (var workflowJobDefinition in _cachedActivities.Keys) - { - if ((GetRuntimeAssemblyPath(workflowJobDefinition) == null) && - (DateTime.Now - workflowJobDefinition.LastUsedDateTime > TimeSpan.FromMilliseconds(activitiesCleanupIntervalMSec))) - { - activitiesToRemove.Add(workflowJobDefinition); - } - } - - foreach (var wfJobDefinition in activitiesToRemove) - { - _cachedActivities.TryRemove(wfJobDefinition, out activity); - } - - // Schedule the next cleanup - _cleanupTimer.Start(); - } - - /// - /// Cache size - /// - private const int _cacheSize = 1000; - - /// - /// Path to Windows folder - /// - private const string WindowsPath = "%windir%\\system32"; - - #endregion Privates - - #region Internal - - internal int CacheSize - { - get { return _cacheSize; } - } - - /// - /// Return the singleton instance. - /// - internal static DefinitionCache Instance - { - get - { - return _instance; - } - } - - internal JobDefinition GetDefinition(Guid instanceId) - { - return _workflowDetailsCache.Keys.FirstOrDefault(def => def.InstanceId == instanceId); - } - - internal string GetWorkflowXaml(WorkflowJobDefinition definition) - { - return definition.Xaml; - } - - internal string GetRuntimeAssemblyPath(WorkflowJobDefinition definition) - { - WorkflowDetails workflowDetail; - - return _workflowDetailsCache.TryGetValue(definition, out workflowDetail) ? workflowDetail.CompiledAssemblyPath : null; - } - - internal string GetRuntimeAssemblyName(WorkflowJobDefinition definition) - { - WorkflowDetails workflowDetail; - - return _workflowDetailsCache.TryGetValue(definition, out workflowDetail) ? workflowDetail.CompiledAssemblyName : null; - } - - internal bool AllowExternalActivity; - - /// - /// Compiles the activity and adds it to the cache before returning it. - /// - /// WorkflowJobDefinition defined to represent a compiled activity. - /// Activity Tree used for external activities - /// - /// indicates if the specified xaml is a Windows workflow - /// Optional, once assigned, only root Workflow will be compiled - /// Activity compiled from xaml given, or retrieved from cache. - /// Null if not found. - internal Activity CompileActivityAndSaveInCache(WorkflowJobDefinition definition, Activity activityTree, Dictionary requiredAssemblies, out bool windowsWorkflow, string rootWorkflowName = null) - { - WorkflowDetails workflowDetail = new WorkflowDetails(); - Activity activity = null; - - // initialize windows workflow to false - windowsWorkflow = false; - - string resultingCompiledAssemblyPath = definition.DependentAssemblyPath; - string modulePath = definition.ModulePath; - string[] dependentWorkflows = definition.DependentWorkflows.ToArray(); - Assembly resultingCompiledAssembly = null; - string resultingCompiledAssemblyName = null; - string xaml = definition.Xaml; - - if (activityTree != null) - { - _tracer.WriteMessage(string.Format(CultureInfo.InvariantCulture, - "DefinitionCache: Caching activity for definition with instance ID: {0}. The activity Tree is passed.", - definition.InstanceId)); - workflowDetail.ActivityTree = activityTree; - activity = activityTree; - } - else if (!String.IsNullOrEmpty(xaml)) - { - // we need to read the xaml from the specified path and compile the same - _tracer.WriteMessage(string.Format(CultureInfo.InvariantCulture, - "DefinitionCache: Caching activity for definition with instance ID: {0}. Xaml is passed", - definition.InstanceId)); - - workflowDetail.ActivityTree = null; - // check if specified workflow is a windows workflow - if (!string.IsNullOrEmpty(modulePath)) - { - string resolvedPath = Environment.ExpandEnvironmentVariables(modulePath); - string resolvedWindowsPath = Environment.ExpandEnvironmentVariables(WindowsPath); - if (resolvedPath.IndexOf(resolvedWindowsPath, StringComparison.CurrentCultureIgnoreCase) != -1) - { - windowsWorkflow = true; - } - } - - if (definition.DependentAssemblyPath == null && dependentWorkflows.Length == 0) - { - activity = ImportWorkflowCommand.ConvertXamlToActivity(xaml); - } - else - { - if (rootWorkflowName == null || (definition.Command == rootWorkflowName)) - { - activity = ImportWorkflowCommand.ConvertXamlToActivity(xaml, dependentWorkflows, - requiredAssemblies, - ref resultingCompiledAssemblyPath, - ref resultingCompiledAssembly, - ref resultingCompiledAssemblyName); - } - else - { - activity = ImportWorkflowCommand.ConvertXamlToActivity(xaml); - } - } - } - - if (activity != null) - { - workflowDetail.IsWindowsActivity = windowsWorkflow; - workflowDetail.CompiledAssemblyPath = resultingCompiledAssemblyPath; - workflowDetail.CompiledAssemblyName = resultingCompiledAssemblyName; - - lock (_syncObject) - { - WorkflowJobDefinition definitionToRemove = - _workflowDetailsCache.Keys.FirstOrDefault(item => item.InstanceId == definition.InstanceId); - if (definitionToRemove != null) - _workflowDetailsCache.Remove(definition); - - _workflowDetailsCache.Add(definition, workflowDetail); - } - - // If cached activity count reaches _cacheSize, - // Removing the cached activity at index 0 and adding the new activity to the activity cache, - // Old logic was to clear 1000 cached activities and recompiling them again when needed. - // - if (_cachedActivities.Count == _cacheSize) - { - Activity removedActivity; - _cachedActivities.TryRemove(_cachedActivities.Keys.ElementAt(0), out removedActivity); - } - - _cachedActivities.TryAdd(definition, activity); - - return activity; - } - - // we should never hit this point under normal course of operations - return null; - } - - /// - /// - /// - /// - /// - /// - internal Activity GetActivityFromCache(WorkflowJobDefinition definition, out bool windowsWorkflow) - { - Activity activity = null; - WorkflowDetails workflowDetail; - - // initialize windowsWorkflow to false - windowsWorkflow = false; - if (_workflowDetailsCache.TryGetValue(definition, out workflowDetail)) - { - // if there is a workflow activity we would have already computed whether it is a - // Windows workflow, set the same - windowsWorkflow = workflowDetail.IsWindowsActivity; - - if (workflowDetail.ActivityTree != null) - { - if (!AllowExternalActivity) - { - Debug.Assert(false, "Product code should not contain passing an activity to definition cache"); - } - - activity = workflowDetail.ActivityTree; - } - - // if a cached value is available return the same - _cachedActivities.TryGetValue(definition, out activity); - if (activity == null) - { - // if activity is not available in cache recompile using info in - // definition cache - activity = CompileActivityAndSaveInCache(definition, null, null, out windowsWorkflow); - } - - definition.LastUsedDateTime = DateTime.Now; - } - - return activity; - } - - /// - /// - /// - /// - /// - /// - internal Activity GetActivityFromCache(string xaml, out WorkflowJobDefinition workflowJobDefinition) - { - workflowJobDefinition = null; - - foreach (var definition in - _workflowDetailsCache.Keys.Where(definition => string.Equals(definition.Xaml, xaml, StringComparison.OrdinalIgnoreCase))) - { - bool windowsWorkflow; - workflowJobDefinition = definition; - return GetActivityFromCache(definition, out windowsWorkflow); - } - - return null; - } - - private readonly object _syncObject = new object(); - - #endregion Internal - - #region Test Helpers - - internal Dictionary WorkflowDetailsCache - { - get { return _workflowDetailsCache; } - } - - internal ConcurrentDictionary ActivityCache - { - get { return _cachedActivities; } - } - - internal void ClearAll() - { - _workflowDetailsCache.Clear(); - _cachedActivities.Clear(); - } - - // only called from test - internal Activity GetActivity(Guid instanceId) - { - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(typeof(WorkflowJobSourceAdapter), - string.Empty, string.Empty, - string.Empty, - WorkflowJobDefinition.EmptyEnumerable, - string.Empty, null, string.Empty) { InstanceId = instanceId }; - - bool windowsWorkflow; - Activity activity = GetActivityFromCache(workflowJobDefinition, out windowsWorkflow) ?? - CompileActivityAndSaveInCache(workflowJobDefinition, null, null, out windowsWorkflow); - - return activity; - } - - //only called from test - internal Activity GetActivity(JobDefinition definition, string xaml) - { - bool windowsWorkflow; - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(definition, string.Empty, - WorkflowJobDefinition.EmptyEnumerable, - string.Empty, - xaml); - Activity activity = GetActivityFromCache(workflowJobDefinition, out windowsWorkflow) ?? - CompileActivityAndSaveInCache(workflowJobDefinition, null, null, out windowsWorkflow); - - return activity; - } - - //only called from test - internal Activity GetActivity(JobDefinition definition, string xaml, string[] dependentWorkflows) - { - bool windowsWorkflow; - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(definition, string.Empty, - dependentWorkflows ?? WorkflowJobDefinition.EmptyEnumerable, - string.Empty, - xaml); - - Activity activity = GetActivityFromCache(workflowJobDefinition, out windowsWorkflow) ?? - CompileActivityAndSaveInCache(workflowJobDefinition, null, null, out windowsWorkflow); - - return activity; - } - - internal Activity GetActivity(JobDefinition definition, out bool windowsWorkflow) - { - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(definition); - - Activity activity = GetActivityFromCache(workflowJobDefinition, out windowsWorkflow) ?? - CompileActivityAndSaveInCache(workflowJobDefinition, null, null, out windowsWorkflow); - - return activity; - } - - /// - /// Remove a cached definition. Needed when functions are removed to release resources. - /// - /// Xaml definition to remove. - /// True if succeeded. - internal bool RemoveCachedActivity(JobDefinition definition) - { - Debug.Assert(definition != null); - - WorkflowJobDefinition workflowJobDefinition = new WorkflowJobDefinition(definition); - Activity activity; - _cachedActivities.TryRemove(workflowJobDefinition, out activity); - lock (_syncObject) - { - if (_workflowDetailsCache.ContainsKey(workflowJobDefinition)) - { - return _workflowDetailsCache.Remove(workflowJobDefinition); - } - } - return false; - } - - internal bool RemoveCachedActivity(Guid instanceId) - { - var definition = new JobDefinition(null, null, null); - definition.InstanceId = instanceId; - return RemoveCachedActivity(definition); - } - - #endregion Test Helpers - } - - internal class CompareBasedOnInstanceId : IEqualityComparer - { - #region IEqualityComparer Members - - public bool Equals(WorkflowJobDefinition x, WorkflowJobDefinition y) - { - return x.InstanceId == y.InstanceId; - } - - public int GetHashCode(WorkflowJobDefinition obj) - { - return obj.InstanceId.GetHashCode(); - } - - #endregion - } - - internal class CompareBasedOnCommand : IEqualityComparer - { - #region IEqualityComparer Members - - public bool Equals(WorkflowJobDefinition x, WorkflowJobDefinition y) - { - bool returnValue = false; - - if (String.Equals(y.ModulePath, x.ModulePath, StringComparison.OrdinalIgnoreCase)) - { - if (string.Equals(y.Command, x.Command, StringComparison.OrdinalIgnoreCase)) - { - returnValue = true; - } - } - - return returnValue; - } - - public int GetHashCode(WorkflowJobDefinition obj) - { - int hashCode = 0; - string moduleQualifiedCommand = string.Empty; - - if (!string.IsNullOrEmpty(obj.ModulePath)) - moduleQualifiedCommand += obj.ModulePath; - - if (!string.IsNullOrEmpty(obj.Command)) - moduleQualifiedCommand += obj.Command; - - hashCode = moduleQualifiedCommand.GetHashCode(); - - return hashCode; - } - - private static readonly CompareBasedOnCommand Comparer = new CompareBasedOnCommand(); - internal static bool Compare(WorkflowJobDefinition x, WorkflowJobDefinition y) - { - return Comparer.Equals(x, y); - } - - #endregion - } - - internal class WorkflowJobDefinition : JobDefinition - { - /// - /// - /// - /// - /// - /// - public WorkflowJobDefinition(Type jobSourceAdapterType, string command, string name) : base(jobSourceAdapterType, command, name) - { - IsScriptWorkflow = false; - } - - internal WorkflowJobDefinition(Type jobSourceAdapterType, string command, string name, string modulePath, - IEnumerable dependentWorkflows, string dependentAssemblyPath, Dictionary requiredAssemblies, string xaml) - : this(jobSourceAdapterType, command, name) - { - _modulePath = modulePath; - _dependentAssemblyPath = dependentAssemblyPath; - _dependentWorkflows.AddRange(dependentWorkflows); - _requiredAssemblies = requiredAssemblies; - _xaml = xaml; - _lastUsedDateTime = DateTime.Now; - } - - internal WorkflowJobDefinition(JobDefinition jobDefinition, string modulePath, - IEnumerable dependentWorkflows, string dependentAssemblyPath, string xaml) - : this(jobDefinition.JobSourceAdapterType, jobDefinition.Command, jobDefinition.Name, modulePath, dependentWorkflows, dependentAssemblyPath, null, xaml) - { - InstanceId = jobDefinition.InstanceId; - } - - internal WorkflowJobDefinition(JobDefinition jobDefinition) - :this(jobDefinition, string.Empty, EmptyEnumerable, string.Empty, string.Empty) - { - - } - - private readonly string _modulePath; - internal string ModulePath - { - get { return _modulePath; } - } - - private readonly List _dependentWorkflows = new List(); - internal List DependentWorkflows - { - get { return _dependentWorkflows; } - } - - private readonly string _dependentAssemblyPath; - internal string DependentAssemblyPath - { - get { return _dependentAssemblyPath; } - } - - private readonly Dictionary _requiredAssemblies; - internal Dictionary RequiredAssemblies - { - get { return _requiredAssemblies; } - } - - private readonly string _xaml; - internal string Xaml - { - get { return _xaml; } - } - - internal bool IsScriptWorkflow { get; set; } - - internal static IEnumerable EmptyEnumerable = new Collection(); - - private DateTime _lastUsedDateTime; - internal DateTime LastUsedDateTime - { - get { return _lastUsedDateTime; } - set { _lastUsedDateTime = value; } - } - - /// - /// Returns the same object is the specified job definition is a - /// WorkflowJobDefinition. If not creates a new one and assigns - /// the same - /// - /// job definition to check - /// WorkflowJobDefinition equivalent - internal static WorkflowJobDefinition AsWorkflowJobDefinition(JobDefinition definition) - { - return definition as WorkflowJobDefinition ?? - (DefinitionCache.Instance.GetDefinition(definition.InstanceId) as WorkflowJobDefinition ?? - new WorkflowJobDefinition(definition)); - } - - /// - /// - /// - /// - /// - public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) - { - base.GetObjectData(info, context); - - throw new NotImplementedException(); - } - - /// - /// Workflow script file name or null if no associated file. - /// - internal string WorkflowScriptFile - { - get; - set; - } - - /// - /// Full workflow script source. - /// - internal string WorkflowFullScript - { - get; - set; - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/AsyncResult.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/AsyncResult.cs deleted file mode 100644 index 2c04a68721a..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/AsyncResult.cs +++ /dev/null @@ -1,174 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Diagnostics; - using System.Threading; - - /// - /// A generic base class for IAsyncResult implementations - /// that wraps a ManualResetEvent. - /// - abstract class FileStoreAsyncResult : IAsyncResult - { - AsyncCallback callback; - object state; - bool completedSynchronously; - bool endCalled; - Exception exception; - bool isCompleted; - ManualResetEvent manualResetEvent; - object thisLock; - - protected FileStoreAsyncResult(AsyncCallback callback, object state) - { - this.callback = callback; - this.state = state; - this.thisLock = new object(); - } - - public object AsyncState - { - get - { - return state; - } - } - - public WaitHandle AsyncWaitHandle - { - get - { - if (manualResetEvent != null) - { - return manualResetEvent; - } - - lock (ThisLock) - { - if (manualResetEvent == null) - { - manualResetEvent = new ManualResetEvent(isCompleted); - } - } - - return manualResetEvent; - } - } - - public bool CompletedSynchronously - { - get - { - return completedSynchronously; - } - } - - public bool IsCompleted - { - get - { - return isCompleted; - } - } - - object ThisLock - { - get - { - return this.thisLock; - } - } - - // Call this version of complete when your asynchronous operation is complete. This will update the state - // of the operation and notify the callback. - protected void Complete(bool completedSynchronously) - { - if (isCompleted) - { - // It is a bug to call Complete twice. - throw new InvalidOperationException(Resources.AsyncResultAlreadyCompleted); - } - - this.completedSynchronously = completedSynchronously; - - if (completedSynchronously) - { - // If we completedSynchronously, then there is no chance that the manualResetEvent was created so - // we do not need to worry about a race condition. - Debug.Assert(this.manualResetEvent == null, "No ManualResetEvent should be created for a synchronous AsyncResult."); - this.isCompleted = true; - } - else - { - lock (ThisLock) - { - this.isCompleted = true; - if (this.manualResetEvent != null) - { - this.manualResetEvent.Set(); - } - } - } - - // If the callback throws, there is a bug in the callback implementation - if (callback != null) - { - callback(this); - } - } - - // Call this version of complete if you raise an exception during processing. In addition to notifying - // the callback, it will capture the exception and store it to be thrown during AsyncResult.End. - protected void Complete(bool completedSynchronously, Exception exception) - { - this.exception = exception; - Complete(completedSynchronously); - } - - // End should be called when the End function for the asynchronous operation is complete. It - // ensures the asynchronous operation is complete, and does some common validation. - protected static TAsyncResult End(IAsyncResult result) - where TAsyncResult : FileStoreAsyncResult - { - if (result == null) - { - throw new ArgumentNullException("result"); - } - - TAsyncResult asyncResult = result as TAsyncResult; - - if (asyncResult == null) - { - throw new ArgumentException(Resources.InvalidAsyncResult); - } - - if (asyncResult.endCalled) - { - throw new InvalidOperationException(Resources.AsyncResultAlreadyEnded); - } - - asyncResult.endCalled = true; - - if (!asyncResult.isCompleted) - { - asyncResult.AsyncWaitHandle.WaitOne(); - } - - if (asyncResult.manualResetEvent != null) - { - asyncResult.manualResetEvent.Close(); - } - - if (asyncResult.exception != null) - { - throw asyncResult.exception; - } - - return asyncResult; - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/FileInstanceStore.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/FileInstanceStore.cs deleted file mode 100644 index 0f95d8ec8b0..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/FileInstanceStore.cs +++ /dev/null @@ -1,225 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Runtime.DurableInstancing; - using System.Activities.DurableInstancing; - using System.Collections.Generic; - using System.Xml.Linq; - using System.IO; - using System.Xml; - using System.Management.Automation.Tracing; - using System.Activities.Persistence; - - internal class FileInstanceStore : InstanceStore - { - private static readonly Tracer StructuredTracer = new Tracer(); - - private readonly PSWorkflowFileInstanceStore _stores; - internal FileInstanceStore(PSWorkflowFileInstanceStore stores) - { - _stores = stores; - } - - protected override IAsyncResult BeginTryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state) - { - StructuredTracer.Correlate(); - - try - { - if (command is SaveWorkflowCommand) - { - return new TypedCompletedAsyncResult(SaveWorkflow(context, (SaveWorkflowCommand)command), callback, state); - } - else if (command is LoadWorkflowCommand) - { - return new TypedCompletedAsyncResult(LoadWorkflow(context, (LoadWorkflowCommand)command), callback, state); - } - else if (command is CreateWorkflowOwnerCommand) - { - return new TypedCompletedAsyncResult(CreateWorkflowOwner(context, (CreateWorkflowOwnerCommand)command), callback, state); - } - else if (command is DeleteWorkflowOwnerCommand) - { - return new TypedCompletedAsyncResult(DeleteWorkflowOwner(context, (DeleteWorkflowOwnerCommand)command), callback, state); - } - return new TypedCompletedAsyncResult(false, callback, state); - } - catch (Exception e) - { - return new TypedCompletedAsyncResult(e, callback, state); - } - } - - protected override bool EndTryCommand(IAsyncResult result) - { - StructuredTracer.Correlate(); - - TypedCompletedAsyncResult exceptionResult = result as TypedCompletedAsyncResult; - if (exceptionResult != null) - { - throw exceptionResult.Data; - } - return TypedCompletedAsyncResult.End(result); - } - - private bool SaveWorkflow(InstancePersistenceContext context, SaveWorkflowCommand command) - { - if (context.InstanceVersion == -1) - { - context.BindAcquiredLock(0); - } - - if (command.CompleteInstance) - { - context.CompletedInstance(); - } - else - { - string instanceType = ""; - - const string InstanceTypeXName = "{urn:schemas-microsoft-com:System.Runtime.DurableInstancing/4.0/metadata}InstanceType"; - InstanceValue instanceTypeInstanceValue; - if (command.InstanceMetadataChanges.TryGetValue(InstanceTypeXName, out instanceTypeInstanceValue)) - { - instanceType = instanceTypeInstanceValue.Value.ToString(); - } - - Dictionary fullInstanceData = new Dictionary(); - fullInstanceData.Add("instanceId", context.InstanceView.InstanceId); - fullInstanceData.Add("instanceOwnerId", context.InstanceView.InstanceOwner.InstanceOwnerId); - fullInstanceData.Add("instanceData", SerializeablePropertyBag(command.InstanceData)); - fullInstanceData.Add("instanceMetadata", SerializeInstanceMetadata(context, command)); - - foreach (KeyValuePair property in command.InstanceMetadataChanges) - { - context.WroteInstanceMetadataValue(property.Key, property.Value); - } - - context.PersistedInstance(command.InstanceData); - - _stores.Save(WorkflowStoreComponents.Definition - | WorkflowStoreComponents.Metadata - | WorkflowStoreComponents.Streams - | WorkflowStoreComponents.TerminatingError - | WorkflowStoreComponents.Timer - | WorkflowStoreComponents.ActivityState - | WorkflowStoreComponents.JobState, - fullInstanceData); - - } - - return true; - } - - private bool LoadWorkflow(InstancePersistenceContext context, LoadWorkflowCommand command) - { - if (command.AcceptUninitializedInstance) - { - return false; - } - - if (context.InstanceVersion == -1) - { - context.BindAcquiredLock(0); - } - - Guid instanceId = context.InstanceView.InstanceId; - Guid instanceOwnerId = context.InstanceView.InstanceOwner.InstanceOwnerId; - - IDictionary instanceData = null; - IDictionary instanceMetadata = null; - - Dictionary fullInstanceData = _stores.LoadWorkflowContext(); - - instanceData = this.DeserializePropertyBag((Dictionary)fullInstanceData["instanceData"]); - instanceMetadata = this.DeserializePropertyBag((Dictionary)fullInstanceData["instanceMetadata"]); - - context.LoadedInstance(InstanceState.Initialized, instanceData, instanceMetadata, null, null); - - return true; - } - - private bool CreateWorkflowOwner(InstancePersistenceContext context, CreateWorkflowOwnerCommand command) - { - Guid instanceOwnerId = Guid.NewGuid(); - context.BindInstanceOwner(instanceOwnerId, instanceOwnerId); - context.BindEvent(HasRunnableWorkflowEvent.Value); - return true; - } - - private bool DeleteWorkflowOwner(InstancePersistenceContext context, DeleteWorkflowOwnerCommand command) - { - return true; - } - - private Dictionary SerializeablePropertyBag(IDictionary source) - { - Dictionary scratch = new Dictionary(); - foreach (KeyValuePair property in source) - { - bool writeOnly = (property.Value.Options & InstanceValueOptions.WriteOnly) != 0; - - if (!writeOnly && !property.Value.IsDeletedValue) - { - scratch.Add(property.Key, property.Value.Value); - } - } - - return scratch; - } - private Dictionary SerializeInstanceMetadata(InstancePersistenceContext context, SaveWorkflowCommand command) - { - Dictionary metadata = null; - - foreach (var property in command.InstanceMetadataChanges) - { - if (!property.Value.Options.HasFlag(InstanceValueOptions.WriteOnly)) - { - if (metadata == null) - { - metadata = new Dictionary(); - // copy current metadata. note that we must rid of InstanceValue as it is not properly serializeable - foreach (var m in context.InstanceView.InstanceMetadata) - { - metadata.Add(m.Key, m.Value.Value); - } - } - - if (metadata.ContainsKey(property.Key)) - { - if (property.Value.IsDeletedValue) metadata.Remove(property.Key); - else metadata[property.Key] = property.Value.Value; - } - else - { - if (!property.Value.IsDeletedValue) metadata.Add(property.Key, property.Value.Value); - } - } - } - - if (metadata == null) - metadata = new Dictionary(); - - return metadata; - } - - private IDictionary DeserializePropertyBag(Dictionary source) - { - Dictionary destination = new Dictionary(); - - foreach (KeyValuePair property in source) - { - destination.Add(property.Key, new InstanceValue(property.Value)); - } - - return destination; - } - - - } -} - \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStoreCryptography.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStoreCryptography.cs deleted file mode 100644 index 0f7bafaf63f..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStoreCryptography.cs +++ /dev/null @@ -1,68 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.IO; - using System.Security.Cryptography; - using System.Management.Automation.Tracing; - - /// - /// This class implements the encrypt and decrypt functionality. - /// - internal class InstanceStoreCryptography - { - /// - /// Tracer initialization. - /// - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// The additional entry for security 'POWERSHELLWORKFLOW' - /// - private static byte[] s_additionalEntropy = { (byte)'P', (byte)'O', (byte)'W', (byte)'E', (byte)'R', (byte)'S', (byte)'H', (byte)'E', (byte)'L', (byte)'L', (byte)'W', (byte)'O', (byte)'R', (byte)'K', (byte)'F', (byte)'L', (byte)'O', (byte)'W' }; - - /// - /// Protect the data. - /// - /// The input data for encryption. - /// Returns encrypted data. - internal static byte[] Protect(byte[] data) - { - try - { - // Encrypt the data using DataProtectionScope.CurrentUser. The result can be decrypted - // only by the same current user. - return ProtectedData.Protect(data, s_additionalEntropy, DataProtectionScope.CurrentUser); - } - catch (CryptographicException e) - { - Tracer.TraceException(e); - - throw e; - } - } - - /// - /// Unprotect data the encrypted data. - /// - /// Encrypted data. - /// Returns decrypted data. - internal static byte[] Unprotect(byte[] data) - { - try - { - return ProtectedData.Unprotect(data, s_additionalEntropy, DataProtectionScope.CurrentUser); - } - catch (CryptographicException e) - { - Tracer.TraceException(e); - - throw e; - } - - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStorePermission.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStorePermission.cs deleted file mode 100644 index 0d29a001b07..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/InstanceStorePermission.cs +++ /dev/null @@ -1,44 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.IO; - using System.Security.Cryptography; - using System.Management.Automation.Tracing; - using System.Security.AccessControl; - using System.Security.Principal; - - /// - /// This class implements the encrypt and decrypt functionality. - /// - internal class InstanceStorePermission - { - internal static void SetDirectoryPermissions(string folderName) - { - string account = WindowsIdentity.GetCurrent().Name; - RemoveInheritablePermissions(folderName); - AddDirectorySecurity(folderName, account, FileSystemRights.Modify, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow); - } - - private static void AddDirectorySecurity(string folderName, string account, FileSystemRights rights, InheritanceFlags inheritance, PropagationFlags propagation, AccessControlType controlType) - { - DirectoryInfo info = new DirectoryInfo(folderName); - DirectorySecurity dSecurity = info.GetAccessControl(); - dSecurity.AddAccessRule(new FileSystemAccessRule(account, rights, inheritance, propagation, controlType)); - info.SetAccessControl(dSecurity); - } - - private static void RemoveInheritablePermissions(string folderName) - { - DirectoryInfo info = new DirectoryInfo(folderName); - DirectorySecurity dSecurity = info.GetAccessControl(); - const bool IsProtected = true; - const bool PreserveInheritance = false; - dSecurity.SetAccessRuleProtection(IsProtected, PreserveInheritance); - info.SetAccessControl(dSecurity); - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedAsyncResult.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedAsyncResult.cs deleted file mode 100644 index dd3a3d2e1e0..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedAsyncResult.cs +++ /dev/null @@ -1,41 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Diagnostics; - using System.Threading; - - /// - /// A strongly typed AsyncResult. - /// - /// - abstract class TypedAsyncResult : FileStoreAsyncResult - { - T data; - - protected TypedAsyncResult(AsyncCallback callback, object state) - : base(callback, state) - { - } - - public T Data - { - get { return data; } - } - - protected void Complete(T data, bool completedSynchronously) - { - this.data = data; - Complete(completedSynchronously); - } - - public static T End(IAsyncResult result) - { - TypedAsyncResult typedResult = FileStoreAsyncResult.End>(result); - return typedResult.Data; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedCompletedAsyncResult.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedCompletedAsyncResult.cs deleted file mode 100644 index 92ce3cde662..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/TypedCompletedAsyncResult.cs +++ /dev/null @@ -1,35 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Diagnostics; - using System.Threading; - - /// - /// A strongly typed AsyncResult that completes as soon as it is instantiated. - /// - /// - class TypedCompletedAsyncResult : TypedAsyncResult - { - public TypedCompletedAsyncResult(T data, AsyncCallback callback, object state) - : base(callback, state) - { - Complete(data, true); - } - - public new static T End(IAsyncResult result) - { - TypedCompletedAsyncResult completedResult = result as TypedCompletedAsyncResult; - - if (completedResult == null) - { - throw new ArgumentException(Resources.InvalidAsyncResult); - } - - return TypedAsyncResult.End(completedResult); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/WorkflowAdditionalStores.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/WorkflowAdditionalStores.cs deleted file mode 100644 index 70b065a4b67..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/FileInstanceStore/WorkflowAdditionalStores.cs +++ /dev/null @@ -1,2039 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Diagnostics; -using System.IO.Compression; -using System.Management.Automation.Remoting; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.IO; - using System.Management.Automation; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Runtime.Serialization; - using System.Xml.Linq; - using System.Runtime.DurableInstancing; - using System.Globalization; - using System.Management.Automation.Tracing; - using System.Activities.DurableInstancing; - using System.Activities.Persistence; - using System.Reflection; - using Microsoft.PowerShell.Commands; - using System.Text; - - internal class PersistenceVersion - { - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private bool saved = false; - private object syncLock = new object(); - - internal Version StoreVersion { get; set; } - internal Version CLRVersion { get; set; } - internal bool EnableEncryption { get; set; } - internal bool EnableCompression { get; set; } - - internal PersistenceVersion(bool enableEncryption, bool enableCompression) - { - StoreVersion = new Version(1, 0); - CLRVersion = Environment.Version; - EnableEncryption = enableEncryption; - EnableCompression = enableCompression; - } - - internal void save(string versionFileName) - { - if (saved) return; - - lock (syncLock) - { - - if (saved) return; - saved = true; - - if (File.Exists(versionFileName)) - return; - - XElement versionXml = new XElement("PersistenceVersion", - new XElement("StoreVersion", StoreVersion), - new XElement("CLRVersion", CLRVersion), - new XElement("EnableEncryption", EnableEncryption), - new XElement("EnableCompression", EnableCompression)); - - versionXml.Save(versionFileName); - } - } - - internal void load(string versionFileName) - { - try - { - if (!File.Exists(versionFileName)) - return; // use the value provided in the constructor - - XElement versionXml = XElement.Load(versionFileName); - - if (versionXml.Name.LocalName.Equals("PersistenceVersion", StringComparison.OrdinalIgnoreCase)) - { - foreach (XElement x in versionXml.Elements()) - { - if (x.Name.LocalName.Equals("StoreVersion", StringComparison.OrdinalIgnoreCase)) - { - this.StoreVersion = new Version(x.Value); - } - else if (x.Name.LocalName.Equals("CLRVersion", StringComparison.OrdinalIgnoreCase)) - { - this.CLRVersion = new Version(x.Value); - } - else if (x.Name.LocalName.Equals("EnableEncryption", StringComparison.OrdinalIgnoreCase)) - { - this.EnableEncryption = Convert.ToBoolean(x.Value, CultureInfo.InvariantCulture); - } - else if (x.Name.LocalName.Equals("EnableCompression", StringComparison.OrdinalIgnoreCase)) - { - this.EnableCompression = Convert.ToBoolean(x.Value, CultureInfo.InvariantCulture); - } - } - } - } - catch (Exception e) - { - //default values will be used - Tracer.WriteMessage(string.Format(CultureInfo.InvariantCulture, "Exception while reading or parsing the version file: {0}", versionFileName)); - Tracer.TraceException(e); - } - } - } - - - - internal enum InternalStoreComponents - { - Streams = 0, - Metadata = 1, - Definition = 2, - Timer = 3, - JobState = 4, - TerminatingError = 5, - Context = 6, - ActivityState = 7, - } - - /// - /// This class implements the functionality for storing the streams data on to the disk. - /// - public class PSWorkflowFileInstanceStore : PSWorkflowInstanceStore - { - # region private variables - - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer etwTracer = new Tracer(); - - private readonly string Streams = "Str"; - private readonly string Error = "Err"; - private readonly string Metadatas = "Meta"; - private readonly string Definition = "Def"; - private readonly string WorkflowState = "Stat"; - - private readonly string Version_xml = "V.xml"; - - private readonly string InputStream_xml = "IS.xml"; - private readonly string OutputStream_xml = "OS.xml"; - private readonly string ErrorStream_xml = "ES.xml"; - private readonly string WarningStream_xml = "WS.xml"; - private readonly string VerboseStream_xml = "VS.xml"; - private readonly string ProgressStream_xml = "PS.xml"; - private readonly string DebugStream_xml = "DS.xml"; - private readonly string InformationStream_xml = "INFS.xml"; - - private readonly string ErrorException_xml = "EE.xml"; - - private readonly string Input_xml = "I.xml"; - private readonly string PSWorkflowCommonParameters_xml = "UI.xml"; - private readonly string JobMetadata_xml = "JM.xml"; - private readonly string PrivateMetadata_xml = "PM.xml"; - private readonly string Timer_xml = "TI.xml"; - - private readonly string WorkflowInstanceState_xml = "WS.xml"; - - private readonly string WorkflowDefinition_xaml = "WD.xaml"; - private readonly string RuntimeAssembly_dll = "RA.dll"; - private readonly string RequiredAssemblies_xml = "RA.xml"; - - private readonly string State_xml = "S.xml"; - - private readonly string ActivityState_xml = "AS.xml"; - - private readonly object _syncLock = new object(); - - private bool firstTimeStoringDefinition; - - private PSWorkflowConfigurationProvider _configuration; - - private Dictionary SavedComponentLengths; - - private long writtenTotalBytes = 0; - - # endregion - - // For testing purpose ONLY - internal static bool TestMode = false; - // For testing purpose ONLY - internal static long ObjectCounter = 0; - - /// - /// Returns all Workflow guids. - /// - /// - public static IEnumerable GetAllWorkflowInstanceIds() - { - PSWorkflowConfigurationProvider _configuration = (PSWorkflowConfigurationProvider) PSWorkflowRuntime.Instance.Configuration; - DirectoryInfo dir = new DirectoryInfo(_configuration.InstanceStorePath); - if (dir.Exists) - { - foreach (DirectoryInfo childDir in dir.GetDirectories()) - { - Guid id; - if (Guid.TryParse(childDir.Name, out id)) - yield return new PSWorkflowId(id); - } - } - } - - - /// - /// PSWorkflowFileInstanceStore ctor - /// - /// - /// - public PSWorkflowFileInstanceStore(PSWorkflowConfigurationProvider configuration, PSWorkflowInstance instance) - : base(instance) - { - if (configuration == null) - throw new ArgumentNullException("configuration"); - - if (TestMode) - { - System.Threading.Interlocked.Increment(ref ObjectCounter); - } - - _configuration = configuration; - firstTimeStoringDefinition = true; - SavedComponentLengths = new Dictionary(); - - bool enableCompression = true; - _disablePersistenceLimits = true; - if (PSSessionConfigurationData.IsServerManager) - { - enableCompression = false; - _disablePersistenceLimits = false; - } - - _version = new PersistenceVersion(_configuration.PersistWithEncryption, enableCompression); - _version.load(Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Version_xml)); - } - - private void SaveVersionFile() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString()); - EnsureInstancePath(storePath); - - _version.save(Path.Combine(storePath, Version_xml)); - } - - private PersistenceVersion _version; - - /// - /// CreateInstanceStore - /// - /// - public override InstanceStore CreateInstanceStore() - { - return new FileInstanceStore(this); - } - - /// - /// CreatePersistenceIOParticipant - /// - /// - public override PersistenceIOParticipant CreatePersistenceIOParticipant() - { - return null; - } - - # region Serialization - - /// - /// To be called from test code only. - /// - /// - internal void CallDoSave(IEnumerable components) - { - DoSave(components); - } - - /// - /// DoSave - /// - /// - protected override void DoSave(IEnumerable components) - { - // no persistence is allowed if the serialization error has occured. - if (serializationErrorHasOccured) - return; - - this.SaveVersionFile(); - - if (_disablePersistenceLimits) - { - DoSave2(components); - return; - } - - lock (_syncLock) - { - long contextLengthNew = 0; - long streamLengthNew = 0; - long metadataLengthNew = 0; - long definitionLengthNew = 0; - long timerLengthNew = 0; - long jobStateLengthNew = 0; - long errorExceptionLengthNew = 0; - long activityStateLengthNew = 0; - - long contextLengthOld = 0; - long streamLengthOld = 0; - long metadataLengthOld = 0; - long definitionLengthOld = 0; - long timerLengthOld = 0; - long jobStateLengthOld = 0; - long errorExceptionLengthOld = 0; - long activityStateLengthOld = 0; - - foreach (object component in components) - { - Type componentType = component.GetType(); - - if(componentType == typeof(Dictionary)) - { - contextLengthNew = this.LoadSerializedContext(component); - contextLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Context) ? - this.SavedComponentLengths[InternalStoreComponents.Context] : - this.GetSavedContextLength(); - - this.SaveSerializedContext(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Context)) - SavedComponentLengths.Remove(InternalStoreComponents.Context); - this.SavedComponentLengths.Add(InternalStoreComponents.Context, contextLengthNew); - } - else if(componentType == typeof(PowerShellStreams)) - { - streamLengthNew = this.LoadSerializedStreamData(component); - streamLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Streams) ? - this.SavedComponentLengths[InternalStoreComponents.Streams] : - this.GetSavedStreamDataLength(); - - this.SaveSerializedStreamData(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Streams)) - SavedComponentLengths.Remove(InternalStoreComponents.Streams); - this.SavedComponentLengths.Add(InternalStoreComponents.Streams, streamLengthNew); - } - else if(componentType == typeof(PSWorkflowContext)) - { - metadataLengthNew = this.LoadSerializedMetadata(component); - metadataLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Metadata) ? - this.SavedComponentLengths[InternalStoreComponents.Metadata] : - this.GetSavedMetadataLength(); - - this.SaveSerializedMetadata(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Metadata)) - SavedComponentLengths.Remove(InternalStoreComponents.Metadata); - this.SavedComponentLengths.Add(InternalStoreComponents.Metadata, metadataLengthNew); - } - else if(componentType == typeof(PSWorkflowDefinition)) - { - if (firstTimeStoringDefinition) - { - definitionLengthNew = this.LoadSerializedDefinition(component); - definitionLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Definition) ? - this.SavedComponentLengths[InternalStoreComponents.Definition] : - this.GetSavedDefinitionLength(); - - this.SaveSerializedDefinition(component); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Definition)) - SavedComponentLengths.Remove(InternalStoreComponents.Definition); - this.SavedComponentLengths.Add(InternalStoreComponents.Definition, definitionLengthNew); - } - } - else if(componentType == typeof(PSWorkflowTimer)) - { - - timerLengthNew = this.LoadSerializedTimer(component as PSWorkflowTimer); - timerLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Timer) ? - this.SavedComponentLengths[InternalStoreComponents.Timer] : - this.GetSavedTimerLength(); - - this.SaveSerializedTimer(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.Timer)) - SavedComponentLengths.Remove(InternalStoreComponents.Timer); - this.SavedComponentLengths.Add(InternalStoreComponents.Timer, timerLengthNew); - } - else if(componentType == typeof(JobState)) - { - jobStateLengthNew = this.LoadSerializedJobState(component); - jobStateLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.JobState) ? - this.SavedComponentLengths[InternalStoreComponents.JobState] : - this.GetSavedJobStateLength(); - - this.SaveSerializedJobState(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.JobState)) - SavedComponentLengths.Remove(InternalStoreComponents.JobState); - this.SavedComponentLengths.Add(InternalStoreComponents.JobState, jobStateLengthNew); - } - else if (component is Exception) - { - errorExceptionLengthNew = this.LoadSerializedErrorException(component); - errorExceptionLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.TerminatingError) ? - this.SavedComponentLengths[InternalStoreComponents.TerminatingError] : - this.GetSavedErrorExceptionLength(); - - this.SaveSerializedErrorException(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.TerminatingError)) - SavedComponentLengths.Remove(InternalStoreComponents.TerminatingError); - this.SavedComponentLengths.Add(InternalStoreComponents.TerminatingError, errorExceptionLengthNew); - } - else if (componentType == typeof(PSWorkflowRemoteActivityState)) - { - activityStateLengthNew = this.LoadSerializedActivityState(component as PSWorkflowRemoteActivityState); - activityStateLengthOld = this.SavedComponentLengths.ContainsKey(InternalStoreComponents.ActivityState) ? - this.SavedComponentLengths[InternalStoreComponents.ActivityState] : - this.GetSavedActivityStateLength(); - - this.SaveSerializedActivityState(); - if (this.SavedComponentLengths.ContainsKey(InternalStoreComponents.ActivityState)) - SavedComponentLengths.Remove(InternalStoreComponents.ActivityState); - this.SavedComponentLengths.Add(InternalStoreComponents.ActivityState, activityStateLengthNew); - } - } - - - // Now verify if the data can be in allowed max persistence limit - long oldValue = ( - contextLengthOld + - streamLengthOld + - metadataLengthOld + - definitionLengthOld + - timerLengthOld + - jobStateLengthOld + - activityStateLengthOld + - errorExceptionLengthOld - ); - - long newValue = ( - contextLengthNew + - streamLengthNew + - metadataLengthNew + - definitionLengthNew + - timerLengthNew + - jobStateLengthNew + - activityStateLengthNew + - errorExceptionLengthNew - ); - - bool allowed = CheckMaxPersistenceSize(oldValue, newValue); - - // if not allowed then write the warning message and continue execution. - if (allowed == false) - { - this.WriteWarning(Resources.PersistenceSizeReached); - etwTracer.PersistenceStoreMaxSizeReached(); - } - } - } - - /// - /// Delete - /// - protected override void DoDelete() - { - lock (_syncLock) - { - InternalDelete(); - } - } - - private void InternalDelete() - { - string instanceFolder = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString()); - if (Directory.Exists(instanceFolder)) - { - try - { - long size = this.GetDirectoryLength(new DirectoryInfo(instanceFolder)); - Directory.Delete(instanceFolder, true); - ReducePersistenceSize(size); - } - catch (IOException e) - { - RetryDelete(e, instanceFolder); - } - catch (UnauthorizedAccessException e) - { - RetryDelete(e, instanceFolder); - } - } - } - - // RetryDelete throws exception if delete fails again, that exception causes the remove job to fail - // - private void RetryDelete(Exception e, string instanceFolder) - { - Tracer.TraceException(e); - Tracer.WriteMessage("Trying to delete one more time."); - - try - { - Directory.Delete(instanceFolder, true); - } - catch (Exception exc) - { - Tracer.TraceException(exc); - - // eating the exceptions as remove job should not throw any errors for deleting the store files. - } - } - - # region Stream - - private ArraySegment serializedInputStreamData; - private ArraySegment serializedOutputStreamData; - private ArraySegment serializedErrorStreamData; - private ArraySegment serializedWarningStreamData; - private ArraySegment serializedVerboseStreamData; - private ArraySegment serializedProgressStreamData; - private ArraySegment serializedDebugStreamData; - private ArraySegment serializedInformationStreamData; - - private long LoadSerializedStreamData(object data) - { - Debug.Assert(data.GetType() == typeof (PowerShellStreams), "The data should be of type workflow stream"); - PowerShellStreams streams = (PowerShellStreams) data; - - this.serializedInputStreamData = Encrypt(SerializeObject(streams.InputStream)); - this.serializedOutputStreamData = Encrypt(SerializeObject(streams.OutputStream)); - this.serializedErrorStreamData = Encrypt(SerializeObject(streams.ErrorStream)); - this.serializedWarningStreamData = Encrypt(SerializeObject(streams.WarningStream)); - this.serializedVerboseStreamData = Encrypt(SerializeObject(streams.VerboseStream)); - this.serializedProgressStreamData = Encrypt(SerializeObject(streams.ProgressStream)); - this.serializedDebugStreamData = Encrypt(SerializeObject(streams.DebugStream)); - this.serializedInformationStreamData = Encrypt(SerializeObject(streams.InformationStream)); - - long size = ( - this.serializedInputStreamData.Count + - this.serializedOutputStreamData.Count + - this.serializedErrorStreamData.Count + - this.serializedWarningStreamData.Count + - this.serializedVerboseStreamData.Count + - this.serializedProgressStreamData.Count + - this.serializedDebugStreamData.Count + - this.serializedInformationStreamData.Count - ); - - return size; - } - - private void EnsureInstancePath(string storePath) - { - if (Directory.Exists(storePath) == false) - { - try - { - Directory.CreateDirectory(storePath); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - InstanceStorePermission.SetDirectoryPermissions(storePath); - } - } - - private void SaveSerializedStreamData() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Streams); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedInputStreamData, Path.Combine(storePath, InputStream_xml)); - SaveToFile(this.serializedOutputStreamData, Path.Combine(storePath, OutputStream_xml)); - SaveToFile(this.serializedErrorStreamData, Path.Combine(storePath, ErrorStream_xml)); - SaveToFile(this.serializedWarningStreamData, Path.Combine(storePath, WarningStream_xml)); - SaveToFile(this.serializedVerboseStreamData, Path.Combine(storePath, VerboseStream_xml)); - SaveToFile(this.serializedProgressStreamData, Path.Combine(storePath, ProgressStream_xml)); - SaveToFile(this.serializedDebugStreamData, Path.Combine(storePath, DebugStream_xml)); - SaveToFile(this.serializedInformationStreamData, Path.Combine(storePath, InformationStream_xml)); - } - - private long GetSavedStreamDataLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Streams); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, InputStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, OutputStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, ErrorStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, WarningStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, VerboseStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, ProgressStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, DebugStream_xml)); - size += (info.Exists) ? info.Length : 0; - - info = new FileInfo(Path.Combine(storePath, InformationStream_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - # region PSWorkflowContext - - private ArraySegment serializedWorkflowParameters; - private ArraySegment serializedPSWorkflowCommonParameters; - private ArraySegment serializedJobMetadata; - private ArraySegment serializedPrivateMetadata; - - private long LoadSerializedMetadata(object data) - { - Debug.Assert(data.GetType() == typeof (PSWorkflowContext), "The data should be of type workflow metadata"); - PSWorkflowContext metadata = (PSWorkflowContext) data; - - this.serializedWorkflowParameters = Encrypt(SerializeObject(metadata.WorkflowParameters)); - this.serializedPSWorkflowCommonParameters = Encrypt(SerializeObject(metadata.PSWorkflowCommonParameters)); - this.serializedJobMetadata = Encrypt(SerializeObject(metadata.JobMetadata)); - this.serializedPrivateMetadata = Encrypt(SerializeObject(metadata.PrivateMetadata)); - - long size = ( - this.serializedWorkflowParameters.Count + - this.serializedPSWorkflowCommonParameters.Count + - this.serializedJobMetadata.Count + - this.serializedPrivateMetadata.Count - ); - - return size; - - } - - private void SaveSerializedMetadata() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedWorkflowParameters, Path.Combine(storePath, Input_xml)); - SaveToFile(this.serializedPSWorkflowCommonParameters,Path.Combine(storePath, PSWorkflowCommonParameters_xml)); - SaveToFile(this.serializedJobMetadata, Path.Combine(storePath, JobMetadata_xml)); - SaveToFile(this.serializedPrivateMetadata, Path.Combine(storePath, PrivateMetadata_xml)); - } - - private long GetSavedMetadataLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(),Metadatas); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, Input_xml)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, PSWorkflowCommonParameters_xml)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, JobMetadata_xml)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, PrivateMetadata_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - # region Error Exception - - private ArraySegment serializedErrorException; - - private long LoadSerializedErrorException(object data) - { - this.serializedErrorException = Encrypt(SerializeObject(data)); - - long size = this.serializedErrorException.Count; - - return size; - } - - private void SaveSerializedErrorException() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Error); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedErrorException, Path.Combine(storePath, ErrorException_xml)); - - } - - private long GetSavedErrorExceptionLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Error); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, ErrorException_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - #region Timer - - private ArraySegment serializedTimerData; - - private long LoadSerializedTimer(PSWorkflowTimer data) - { - this.serializedTimerData = Encrypt(SerializeObject(data.GetSerializedData())); - - long size = this.serializedTimerData.Count; - - return size; - } - - private void SaveSerializedTimer() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedTimerData, Path.Combine(storePath, Timer_xml)); - } - - private long GetSavedTimerLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, Timer_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - #endregion - - # region PSWorkflowDefinition - private ArraySegment serializedRequiredAssemblies; - private long LoadSerializedDefinition(object data) - { - Debug.Assert(data.GetType() == typeof (PSWorkflowDefinition), "The data should be of type workflow definition"); - PSWorkflowDefinition definition = (PSWorkflowDefinition) data; - - string WorkflowXaml = definition.WorkflowXaml; - string runtimeAssemblyPath = definition.RuntimeAssemblyPath; - - long size = WorkflowXaml == null ? 0 : WorkflowXaml.Length; - - if (!string.IsNullOrEmpty(runtimeAssemblyPath) && File.Exists(runtimeAssemblyPath)) - { - FileInfo file = new FileInfo(runtimeAssemblyPath); - size += file.Length; - } - - if(definition.RequiredAssemblies != null) - { - this.serializedRequiredAssemblies = Encrypt(SerializeObject(definition.RequiredAssemblies)); - size += this.serializedRequiredAssemblies.Count; - } - - return size; - } - - private void SaveSerializedDefinition(object data) - { - Debug.Assert(data.GetType() == typeof (PSWorkflowDefinition), "The data should be of type workflow definition"); - PSWorkflowDefinition definition = (PSWorkflowDefinition) data; - - if (firstTimeStoringDefinition) - { - firstTimeStoringDefinition = false; - - string WorkflowXaml = definition.WorkflowXaml; - string runtimeAssemblyPath = definition.RuntimeAssemblyPath; - - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Definition); - EnsureInstancePath(storePath); - - try - { - File.WriteAllText(Path.Combine(storePath, WorkflowDefinition_xaml), WorkflowXaml); - - if (!string.IsNullOrEmpty(runtimeAssemblyPath) && File.Exists(runtimeAssemblyPath)) - { - string radllPath = Path.Combine(storePath, RuntimeAssembly_dll); - - // Copy if file locations are different. - // Both paths are same when a suspended job from another console is resumed and end persistence happened. - if (!String.Equals(radllPath, runtimeAssemblyPath, StringComparison.OrdinalIgnoreCase)) - { - File.Copy(runtimeAssemblyPath, Path.Combine(storePath, RuntimeAssembly_dll)); - } - } - - if (definition.RequiredAssemblies != null) - { - if(_disablePersistenceLimits) - { - SerializeAndSaveToFile(definition.RequiredAssemblies, Path.Combine(storePath, RequiredAssemblies_xml)); - } - else - { - SaveToFile(this.serializedRequiredAssemblies, Path.Combine(storePath, RequiredAssemblies_xml)); - } - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - } - - private long GetSavedDefinitionLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Definition); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, WorkflowDefinition_xaml)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, RuntimeAssembly_dll)); - size += (info.Exists) ? info.Length : 0; - info = new FileInfo(Path.Combine(storePath, RequiredAssemblies_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - # region Job State - - private ArraySegment serializedJobState; - - private long LoadSerializedJobState(object data) - { - this.serializedJobState = Encrypt(SerializeObject(data)); - - long size = this.serializedJobState.Count; - - return size; - } - - private void SaveSerializedJobState() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - EnsureInstancePath(storePath); - - Debug.Assert(PSWorkflowInstance.PSWorkflowJob != null, - "When SerializeWorkflowJobStateToStore is called, the _job instance needs to be populated"); - SaveToFile(this.serializedJobState, Path.Combine(storePath, WorkflowInstanceState_xml)); - } - - private long GetSavedJobStateLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, WorkflowInstanceState_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - #region ActivityState - private ArraySegment serializedActivityState; - private long LoadSerializedActivityState(PSWorkflowRemoteActivityState data) - { - this.serializedActivityState = Encrypt(SerializeObject(data.GetSerializedData())); - return this.serializedActivityState.Count; - } - - private void SaveSerializedActivityState() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedActivityState, Path.Combine(storePath, ActivityState_xml)); - } - - private long GetSavedActivityStateLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, ActivityState_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - #endregion ActivityState - - - # region WorkflowContext - - private ArraySegment serializedContext; - - private long LoadSerializedContext(object data) - { - this.serializedContext = Encrypt(SerializeObject(data)); - - long size = this.serializedContext.Count; - - return size; - } - - private void SaveSerializedContext() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), WorkflowState); - EnsureInstancePath(storePath); - - SaveToFile(this.serializedContext, Path.Combine(storePath, State_xml)); - } - - private long GetSavedContextLength() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), WorkflowState); - - long size = 0; - - if (Directory.Exists(storePath)) - { - FileInfo info = new FileInfo(Path.Combine(storePath, State_xml)); - size += (info.Exists) ? info.Length : 0; - } - - return size; - } - - # endregion - - - # endregion - - # region Deserialization - - private PowerShellStreams DeserializeWorkflowStreamsFromStore() - { - PowerShellStreams stream = new PowerShellStreams(null); - - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Streams); - - if (Directory.Exists(storePath)) - { - if (_disablePersistenceLimits) - { - stream.InputStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, InputStream_xml)); - stream.OutputStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, OutputStream_xml)); - stream.ErrorStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, ErrorStream_xml)); - stream.WarningStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, WarningStream_xml)); - stream.VerboseStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, VerboseStream_xml)); - stream.ProgressStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, ProgressStream_xml)); - stream.DebugStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, DebugStream_xml)); - stream.InformationStream = - (PSDataCollection) - LoadFromFileAndDeserialize(Path.Combine(storePath, InformationStream_xml)); - } - else - { - stream.InputStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, InputStream_xml)))); - stream.OutputStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, OutputStream_xml)))); - stream.ErrorStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, ErrorStream_xml)))); - stream.WarningStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, WarningStream_xml)))); - stream.VerboseStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, VerboseStream_xml)))); - stream.ProgressStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, ProgressStream_xml)))); - stream.DebugStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, DebugStream_xml)))); - stream.InformationStream = - (PSDataCollection) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, InformationStream_xml)))); - } - } - - return stream; - } - - private Exception DeserializeWorkflowErrorExceptionFromStore() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Error); - - if (Directory.Exists(storePath) == false) - return null; - - return _disablePersistenceLimits - ? (Exception)LoadFromFileAndDeserialize(Path.Combine(storePath, ErrorException_xml)) - : (Exception) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, ErrorException_xml)))); - } - - private object DeserializeWorkflowTimerFromStore() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - if (Directory.Exists(storePath) == false) - return null; - - return _disablePersistenceLimits - ? LoadFromFileAndDeserialize(Path.Combine(storePath, Timer_xml)) - : DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, Timer_xml)))); - } - - private PSWorkflowContext DeserializeWorkflowMetadataFromStore() - { - PSWorkflowContext metadata = new PSWorkflowContext(); - - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - if (Directory.Exists(storePath) == false) - return metadata; - - if (_disablePersistenceLimits) - { - metadata.WorkflowParameters = - (Dictionary) - LoadFromFileAndDeserialize(Path.Combine(storePath, Input_xml)); - metadata.PSWorkflowCommonParameters = - (Dictionary) - LoadFromFileAndDeserialize(Path.Combine(storePath, PSWorkflowCommonParameters_xml)); - metadata.JobMetadata = - (Dictionary) - LoadFromFileAndDeserialize(Path.Combine(storePath, JobMetadata_xml)); - metadata.PrivateMetadata = - (Dictionary) - LoadFromFileAndDeserialize(Path.Combine(storePath, PrivateMetadata_xml)); - } - else - { - metadata.WorkflowParameters = - (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, Input_xml)))); - metadata.PSWorkflowCommonParameters = - (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, PSWorkflowCommonParameters_xml)))); - metadata.JobMetadata = - (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, JobMetadata_xml)))); - metadata.PrivateMetadata = - (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, PrivateMetadata_xml)))); - } - return metadata; - } - - /// - /// To be called from test code ONLY. - /// - /// - internal IEnumerable CallDoLoad(IEnumerable componentTypes) - { - return DoLoad(componentTypes); - } - - /// - /// DoLoad - /// - /// - protected override IEnumerable DoLoad(IEnumerable componentTypes) - { - Collection loadedComponents = new Collection(); - - lock (_syncLock) - { - foreach (Type componentType in componentTypes) - { - if (componentType == typeof(JobState)) - { - JobState? state = DeserializeWorkflowInstanceStateFromStore(); - if (state != null) - { - JobState jobState = (JobState)state; - if (jobState == JobState.Running || jobState == JobState.Suspended || jobState == JobState.Suspending || jobState == JobState.Stopping || jobState == JobState.NotStarted) - jobState = JobState.Suspended; - - loadedComponents.Add(jobState); - } - } - else if (componentType == typeof(Dictionary)) - { - Dictionary context = DeserializeContextFromStore(); - - if (context != null) - loadedComponents.Add(context); - } - else if (componentType == typeof(PSWorkflowDefinition)) - { - PSWorkflowDefinition workflowDefinition = DeserializeWorkflowDefinitionFromStore(); - - if (workflowDefinition != null) - loadedComponents.Add(workflowDefinition); - } - else if (componentType == typeof(Exception)) - { - Exception exception = DeserializeWorkflowErrorExceptionFromStore(); - - if (exception != null) - loadedComponents.Add(exception); - } - else if (componentType == typeof(PSWorkflowContext)) - { - loadedComponents.Add(DeserializeWorkflowMetadataFromStore()); - } - else if (componentType == typeof(PowerShellStreams)) - { - loadedComponents.Add((PowerShellStreams)DeserializeWorkflowStreamsFromStore()); - } - else if (componentType == typeof(PSWorkflowTimer)) - { - object value = DeserializeWorkflowTimerFromStore(); - loadedComponents.Add( value == null ? new PSWorkflowTimer(PSWorkflowInstance) : new PSWorkflowTimer(PSWorkflowInstance, value)); - } - else if (componentType == typeof(PSWorkflowRemoteActivityState)) - { - Dictionary>> value = DeserializeActivityStateFromStore(); - loadedComponents.Add(value == null ? new PSWorkflowRemoteActivityState(this) : new PSWorkflowRemoteActivityState(this, value)); - } - } - } - - return loadedComponents; - } - - internal JobState? DeserializeWorkflowInstanceStateFromStore() - { - string filePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas, WorkflowInstanceState_xml); - - if (File.Exists(filePath) == false) - { - return null; - } - - return _disablePersistenceLimits - ? (JobState) LoadFromFileAndDeserialize(filePath) - : (JobState) DeserializeObject(Decrypt(LoadFromFile(filePath))); - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] - private PSWorkflowDefinition DeserializeWorkflowDefinitionFromStore() - { - string WorkflowXaml = string.Empty; - string runtimeAssemblyPath = null; - Dictionary requiredAssemblies = null; - - try - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Definition); - - if (Directory.Exists(storePath) == false) - return null; - - WorkflowXaml = File.ReadAllText(Path.Combine(storePath, WorkflowDefinition_xaml)); - - if (File.Exists(Path.Combine(storePath, RuntimeAssembly_dll))) - { - runtimeAssemblyPath = Path.Combine(storePath, RuntimeAssembly_dll); - } - - if (File.Exists(Path.Combine(storePath, RequiredAssemblies_xml))) - { - requiredAssemblies = _disablePersistenceLimits - ? (Dictionary) LoadFromFileAndDeserialize(Path.Combine(storePath, RequiredAssemblies_xml)) - : (Dictionary) DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, RequiredAssemblies_xml)))); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - var definition = new PSWorkflowDefinition(null, WorkflowXaml, runtimeAssemblyPath, requiredAssemblies); - - // Load required assemblies - if (requiredAssemblies != null && requiredAssemblies.Values.Count > 0) - { - foreach (string requiredAssembly in requiredAssemblies.Values) - { - try - { - // Try first from the GAC - Assembly.Load(requiredAssembly); - } - catch (IOException) - { - try - { - // And second by path - Assembly.LoadFrom(requiredAssembly); - } - catch (IOException) - { - // Finally, by relative path - if (System.Management.Automation.Runspaces.Runspace.DefaultRunspace != null) - { - using (System.Management.Automation.PowerShell nestedPs = - System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) - { - nestedPs.AddCommand("Get-Location"); - PathInfo result = nestedPs.Invoke()[0]; - - string currentLocation = result.ProviderPath; - - try - { - Assembly.LoadFrom(Path.Combine(currentLocation, requiredAssembly)); - } - catch (IOException) - { - } - } - } - } - } - } - } - - if (definition.Workflow == null && string.IsNullOrEmpty(definition.WorkflowXaml) == false) - { - if (string.IsNullOrEmpty(definition.RuntimeAssemblyPath)) - { - definition.Workflow = ImportWorkflowCommand.ConvertXamlToActivity(definition.WorkflowXaml); - } - else - { - Assembly assembly = Assembly.LoadFrom(definition.RuntimeAssemblyPath); - string assemblyName = assembly.GetName().Name; - string assemblyPath = definition.RuntimeAssemblyPath; - definition.Workflow = ImportWorkflowCommand.ConvertXamlToActivity(definition.WorkflowXaml, null, requiredAssemblies, ref assemblyPath, ref assembly, ref assemblyName); - } - } - - return definition; - } - - internal Dictionary DeserializeContextFromStore() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), WorkflowState); - - if (Directory.Exists(storePath) == false) - return null; - - Dictionary context = _disablePersistenceLimits - ? (Dictionary) LoadFromFileAndDeserialize(Path.Combine(storePath, State_xml)) - : (Dictionary) - DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, State_xml)))); - return context; - } - - - internal Dictionary>> DeserializeActivityStateFromStore() - { - string storePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString(), Metadatas); - - if (Directory.Exists(storePath) == false) - return null; - - return _disablePersistenceLimits - ? (Dictionary>>)LoadFromFileAndDeserialize(Path.Combine(storePath, ActivityState_xml)) - : (Dictionary>>)DeserializeObject(Decrypt(LoadFromFile(Path.Combine(storePath, ActivityState_xml)))); - } - - # endregion - - # region Helper Methods - - private static readonly byte[] NullArray = {(byte) 'N', (byte) 'U', (byte) 'L', (byte) 'L'}; - private static readonly byte[] EncryptFalse = {(byte) 'F'}; - - internal ArraySegment SerializeObject(object source) - { - if (_version.EnableCompression) - return SerializeObject2(source); - - ArraySegment scratch = new ArraySegment(NullArray); - - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - - using (MemoryStream outputStream = new MemoryStream()) - { - serializer.WriteObject(outputStream, source); - - scratch = new ArraySegment(outputStream.GetBuffer(), 0, (int)outputStream.Length); - } - } - catch (SerializationException e) - { - this.ThrowErrorOrWriteWarning(e); - } - catch (InvalidDataContractException e) - { - this.ThrowErrorOrWriteWarning(e); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - return scratch; - } - - private void WriteWarning(string warningMessage) - { - Tracer.WriteMessage("WorkflowAdditionalStores", "WriteWarning", this.PSWorkflowInstance.Id, warningMessage); - - if (PSWorkflowInstance.Streams.WarningStream.IsOpen) - { - PSWorkflowInstance.Streams.WarningStream.Add(new WarningRecord(warningMessage)); - } - } - - private void ThrowErrorOrWriteWarning(Exception e) - { - if (PSSessionConfigurationData.IsServerManager) - { - string warningMessage = string.Format(CultureInfo.CurrentCulture, Resources.SerializationWarning, e.Message); - this.WriteWarning(warningMessage); - } - else - { - // there is no point in hanging around partial data in the store so deleting the store. - // and no further persistence allowed after this point - this.InternalDelete(); - serializationErrorHasOccured = true; - - SerializationException exception = new SerializationException(Resources.SerializationErrorException, e); - throw exception; - } - } - private bool serializationErrorHasOccured = false; - - internal object DeserializeObject(ArraySegment source) - { - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - using (MemoryStream inputStream = new MemoryStream(source.Array, source.Offset, source.Count)) - { - object scratch = serializer.ReadObject(inputStream); - - return scratch; - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - - /// - /// Encrypt - /// - /// - /// - protected internal virtual ArraySegment Encrypt(ArraySegment source) - { - if (_version.EnableCompression) - return Encrypt2(source); - - if (!_version.EnableEncryption) - { - byte[] data = new byte[source.Count + 1]; - data[0] = (byte) 'F'; - Buffer.BlockCopy(source.Array, 0, data, 1, source.Count); - - ArraySegment scratch = new ArraySegment(data, 0, data.Length); - - return scratch; - } - else - { - byte[] data = new byte[source.Count]; - Buffer.BlockCopy(source.Array, 0, data, 0, source.Count); - byte[] encryptedData = InstanceStoreCryptography.Protect(data); - - byte[] finalData = new byte[encryptedData.Length + 1]; - finalData[0] = (byte) 'T'; - Buffer.BlockCopy(encryptedData, 0, finalData, 1, encryptedData.Length); - - ArraySegment scratch = new ArraySegment(finalData, 0, encryptedData.Length + 1); - - return scratch; - } - } - - /// - /// Decrypt - /// - /// - /// - protected internal virtual ArraySegment Decrypt(ArraySegment source) - { - bool localEncrypt = false; - if (source.Array[0] == 'T') - { - localEncrypt = true; - } - - if (!localEncrypt) - { - ArraySegment scratch = new ArraySegment(source.Array, 1, source.Count - 1); - - return scratch; - } - else - { - byte[] data = new byte[source.Count - 1]; - Buffer.BlockCopy(source.Array, 1, data, 0, source.Count - 1); - - byte[] decryptedData = InstanceStoreCryptography.Unprotect(data); - - ArraySegment scratch = new ArraySegment(decryptedData, 0, decryptedData.Length); - - return scratch; - } - } - - private void SaveToFile(ArraySegment source, string filePath) - { - if (_version.EnableCompression) - { - SaveToFile2(source, filePath); - return; - } - try - { - using (FileStream stream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) - { - stream.Write(source.Array, 0, source.Count); - stream.Flush(); - stream.Close(); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - - private ArraySegment LoadFromFile(string filePath) - { - if (_version.EnableCompression) - return LoadFromFile2(filePath); - - ArraySegment tmpBuf = new ArraySegment(); - - try - { - using (FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None)) - { - if (stream.Length != 0) - { - byte[] buf = new byte[stream.Length]; - stream.Read(buf, 0, (int) stream.Length); - tmpBuf = new ArraySegment(buf, 0, (int) stream.Length); - } - - stream.Close(); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - return tmpBuf; - } - - #endregion - - - #region HighPerf Changes - - private bool _disablePersistenceLimits = false; - - internal ArraySegment SerializeObject2(object source) - { - ArraySegment scratch = new ArraySegment(NullArray); - - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - - using (MemoryStream memoryStream = new MemoryStream()) - { - serializer.WriteObject(memoryStream, source); - - byte[] data = new byte[memoryStream.Length + 1]; - data[0] = (byte) 'F'; - Buffer.BlockCopy(memoryStream.GetBuffer(), 0, data, 1, (int) memoryStream.Length); - scratch = new ArraySegment(data, 0, data.Length); - memoryStream.Close(); - } - } - catch (SerializationException e) - { - this.ThrowErrorOrWriteWarning(e); - } - catch (InvalidDataContractException e) - { - this.ThrowErrorOrWriteWarning(e); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - return scratch; - } - - internal object DeserializeObject2(ArraySegment source) - { - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - using (MemoryStream inputStream = new MemoryStream(source.Array, source.Offset, source.Count)) - { - object scratch = serializer.ReadObject(inputStream); - - return scratch; - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - - /// - /// Serialize an object and directly write to the file - /// - /// object to serialize - /// file to write to - /// number of bytes written - internal int SerializeAndSaveToFile(object source, string filePath) - { - // if there is encryption involved we follow the old non performant - // code path - if (_version.EnableEncryption) - { - ArraySegment serializedData = Encrypt(SerializeObject(source)); - SaveToFile2(serializedData, filePath); - return serializedData.Count; - } - - FileStream stream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None); - GZipStream compressedStream = new GZipStream(stream, CompressionMode.Compress); - - bool sError = false; - Exception sException = null; - - try - { - XmlObjectSerializer serializer = new NetDataContractSerializer(); - compressedStream.Write(EncryptFalse, 0, 1); - serializer.WriteObject(compressedStream, source); - return (int) stream.Length; - } - catch (SerializationException e) - { - sError = true; - sException = e; - } - catch (InvalidDataContractException e) - { - sError = true; - sException = e; - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - finally - { - compressedStream.Close(); - stream.Close(); - compressedStream.Dispose(); - stream.Dispose(); - } - - // this is needed because we want all the streams to closed before executing this method. - if(sError == true) - this.ThrowErrorOrWriteWarning(sException); - - return 0; - } - - internal object LoadFromFileAndDeserialize(string filePath) - { - FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None); - try - { - if (stream.Length != 0) - { - GZipStream decompressedStream = new GZipStream(stream, CompressionMode.Decompress); - - try - { - byte[] marker = new byte[1]; - decompressedStream.Read(marker, 0, 1); - - if (marker[0] == 'T') - { - // if there is encryption involved we follow the old non performant - // code path - decompressedStream.Close(); - stream.Close(); - ArraySegment serializedContents = LoadFromFile2(filePath); - return DeserializeObject2(Decrypt(serializedContents)); - } - - XmlObjectSerializer serializer = new NetDataContractSerializer(); - return serializer.ReadObject(decompressedStream); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - finally - { - decompressedStream.Close(); - decompressedStream.Dispose(); - } - } - } - finally - { - stream.Close(); - stream.Dispose(); - } - return null; - } - - private void SaveToFile2(ArraySegment source, string filePath) - { - try - { - using (FileStream stream = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) - { - using (GZipStream compressedStream = new GZipStream(stream, CompressionMode.Compress)) - { - compressedStream.Write(source.Array, 0, source.Count); - compressedStream.Flush(); - compressedStream.Close(); - } - stream.Close(); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - - private ArraySegment LoadFromFile2(string filePath) - { - ArraySegment tmpBuf = new ArraySegment(); - - try - { - using (FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None)) - { - if (stream.Length != 0) - { - using (GZipStream decompressedStream = new GZipStream(stream, CompressionMode.Decompress)) - { - using (MemoryStream memoryStream = new MemoryStream()) - { - decompressedStream.CopyTo(memoryStream); - tmpBuf = new ArraySegment(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); - decompressedStream.Close(); - memoryStream.Close(); - } - } - } - - stream.Close(); - } - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - return tmpBuf; - } - - internal void DoSave2(IEnumerable components) - { - lock (_syncLock) - { - string storePath; - long totalBytesWritten = 0; - - if (writtenTotalBytes == 0) - { - writtenTotalBytes = this.GetSavedContextLength() + - this.GetSavedStreamDataLength() + - this.GetSavedMetadataLength() + - this.GetSavedDefinitionLength() + - this.GetSavedTimerLength() + - this.GetSavedJobStateLength() + - this.GetSavedErrorExceptionLength(); - } - - foreach (object component in components) - { - Type componentType = component.GetType(); - - if (componentType == typeof(Dictionary)) - { - CreateAndEnsureInstancePath(WorkflowState, out storePath); - - totalBytesWritten += SerializeAndSaveToFile(component, Path.Combine(storePath, State_xml)); - } - else if (componentType == typeof(PowerShellStreams)) - { - CreateAndEnsureInstancePath(Streams, out storePath); - - Debug.Assert(component.GetType() == typeof(PowerShellStreams), - "The data should be of type workflow stream"); - PowerShellStreams streams = (PowerShellStreams)component; - - totalBytesWritten += SerializeAndSaveToFile(streams.InputStream, Path.Combine(storePath, InputStream_xml)); - - long bytesWritten = 0; - bytesWritten = SerializeAndSaveToFile(streams.OutputStream, - Path.Combine(storePath, OutputStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.ErrorStream, Path.Combine(storePath, ErrorStream_xml)); - totalBytesWritten += bytesWritten; - - - bytesWritten = SerializeAndSaveToFile(streams.WarningStream, - Path.Combine(storePath, WarningStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.VerboseStream, - Path.Combine(storePath, VerboseStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.ProgressStream, - Path.Combine(storePath, ProgressStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.DebugStream, Path.Combine(storePath, DebugStream_xml)); - totalBytesWritten += bytesWritten; - - bytesWritten = SerializeAndSaveToFile(streams.InformationStream, Path.Combine(storePath, InformationStream_xml)); - totalBytesWritten += bytesWritten; - } - else if (componentType == typeof(PSWorkflowContext)) - { - CreateAndEnsureInstancePath(Metadatas, out storePath); - - Debug.Assert(component.GetType() == typeof(PSWorkflowContext), - "The data should be of type workflow metadata"); - PSWorkflowContext metadata = (PSWorkflowContext)component; - - totalBytesWritten += SerializeAndSaveToFile(metadata.WorkflowParameters, Path.Combine(storePath, Input_xml)); - totalBytesWritten += SerializeAndSaveToFile(metadata.PSWorkflowCommonParameters, - Path.Combine(storePath, PSWorkflowCommonParameters_xml)); - totalBytesWritten += SerializeAndSaveToFile(metadata.JobMetadata, Path.Combine(storePath, JobMetadata_xml)); - totalBytesWritten += SerializeAndSaveToFile(metadata.PrivateMetadata, - Path.Combine(storePath, PrivateMetadata_xml)); - } - else if (componentType == typeof(PSWorkflowDefinition)) - { - if (firstTimeStoringDefinition) - { - Debug.Assert(component.GetType() == typeof(PSWorkflowDefinition), - "The data should be of type workflow definition"); - PSWorkflowDefinition definition = (PSWorkflowDefinition)component; - - // there is no serialization involved, just storing xamls and reference - // assemblies. So existing method is good - SaveSerializedDefinition(definition); - - totalBytesWritten += this.GetSavedDefinitionLength(); - } - } - else if (componentType == typeof(PSWorkflowTimer)) - { - // timer is stored in the metadatas folder - CreateAndEnsureInstancePath(Metadatas, out storePath); - - PSWorkflowTimer data = (PSWorkflowTimer)component; - totalBytesWritten += SerializeAndSaveToFile(data.GetSerializedData(), Path.Combine(storePath, Timer_xml)); - } - else if (componentType == typeof(JobState)) - { - CreateAndEnsureInstancePath(Metadatas, out storePath); - totalBytesWritten += SerializeAndSaveToFile(component, Path.Combine(storePath, WorkflowInstanceState_xml)); - } - else if (component is Exception) - { - CreateAndEnsureInstancePath(Error, out storePath); - totalBytesWritten += SerializeAndSaveToFile(component, Path.Combine(storePath, ErrorException_xml)); - } - else if (component is PSWorkflowRemoteActivityState) - { - CreateAndEnsureInstancePath(Metadatas, out storePath); - - PSWorkflowRemoteActivityState data = (PSWorkflowRemoteActivityState) component; - totalBytesWritten += SerializeAndSaveToFile(data.GetSerializedData(), Path.Combine(storePath, ActivityState_xml)); - } - } - - long oldValueTotalBytesWritten = writtenTotalBytes; - writtenTotalBytes = totalBytesWritten; - - bool allowed = CheckMaxPersistenceSize(oldValueTotalBytesWritten, writtenTotalBytes); - - // if not allowed then write the warning message and continue execution. - if (allowed == false) - { - this.WriteWarning(Resources.PersistenceSizeReached); - etwTracer.PersistenceStoreMaxSizeReached(); - } - } - } - - private string WorkflowStorePath - { - get - { - // it is fine if this segment is not thread-safe - if (String.IsNullOrEmpty(_workflowStorePath)) - { - _workflowStorePath = Path.Combine(_configuration.InstanceStorePath, PSWorkflowInstance.Id.ToString()); - } - return _workflowStorePath; - } - } - - private string _workflowStorePath; - - private void CreateAndEnsureInstancePath(string subPath, out string storePath) - { - storePath = Path.Combine(WorkflowStorePath, subPath); - EnsureInstancePath(storePath); - } - - private ArraySegment Encrypt2(ArraySegment source) - { - if (!_version.EnableEncryption) - { - return source; - } - byte[] data = new byte[source.Count - 1]; - Buffer.BlockCopy(source.Array, 1, data, 0, source.Count - 1); - - byte[] encryptedData = InstanceStoreCryptography.Protect(data); - byte[] finalData = new byte[encryptedData.Length + 1]; - finalData[0] = (byte)'T'; - Buffer.BlockCopy(encryptedData, 0, finalData, 1, encryptedData.Length); - - ArraySegment scratch = new ArraySegment(finalData, 0, encryptedData.Length + 1); - - return scratch; - } - - #endregion HighPerf Changes - - private static readonly object MaxPersistenceStoreSizeLock = new object(); - internal static long CurrentPersistenceStoreSize = 0; - private static bool _firstTimeCalculatingCurrentStoreSize = true; - - private bool CheckMaxPersistenceSize(long oldValue, long newValue) - { - CalculatePersistenceStoreSizeForFirstTime(); // should not be in lock - - lock (MaxPersistenceStoreSizeLock) - { - long calculatedStoreSize = CurrentPersistenceStoreSize - oldValue + newValue; - - long endpointValueInBytes = _configuration.MaxPersistenceStoreSizeGB*1024*1024*1024; - - if (calculatedStoreSize < endpointValueInBytes) - { - CurrentPersistenceStoreSize = calculatedStoreSize; - return true; - } - else - { - return false; - } - } - } - - private void ReducePersistenceSize(long value) - { - CalculatePersistenceStoreSizeForFirstTime(); // should not be in lock - - lock (MaxPersistenceStoreSizeLock) - { - CurrentPersistenceStoreSize -= value; - } - } - - internal void CalculatePersistenceStoreSizeForFirstTime() - { - // there is no need to calculate the store for the first time because - // there might be possibility of one process trying to get the length - // and other is trying to delete it. - - if (this._configuration.IsDefaultStorePath) - return; - - if (_firstTimeCalculatingCurrentStoreSize == false) - return; - - lock (MaxPersistenceStoreSizeLock) - { - if (_firstTimeCalculatingCurrentStoreSize == false) - return; - - _firstTimeCalculatingCurrentStoreSize = false; - CurrentPersistenceStoreSize = GetDirectoryLength(new DirectoryInfo(_configuration.InstanceStorePath)); - } - } - - private long GetDirectoryLength(DirectoryInfo directory) - { - long size = 0; - - try - { - if (directory.Exists) - { - DirectoryInfo[] dirs = directory.GetDirectories(); - FileInfo[] files = directory.GetFiles(); - foreach (FileInfo file in files) - { - size += file.Exists ? file.Length : 0; - } - foreach (DirectoryInfo dir in dirs) - { - size += GetDirectoryLength(dir); - } - } - } - // It is safe of absorb an exception here because there might be a possibility that the multiple - // processes are try work on the same endpoint or default end point store folder. - // There is a possibility that one process has deleted the a workflow store and the other one is trying to access it for calculating its store size. - // And we will set the flag to make sure we calculate the size next time. - catch (DirectoryNotFoundException e) - { - Tracer.TraceException(e); - _firstTimeCalculatingCurrentStoreSize = true; - } - catch (FileNotFoundException e) - { - Tracer.TraceException(e); - _firstTimeCalculatingCurrentStoreSize = true; - } - catch (UnauthorizedAccessException e) - { - Tracer.TraceException(e); - _firstTimeCalculatingCurrentStoreSize = true; - } - - return size; - } - } -} - ; diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/GlobalConfiguration.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/GlobalConfiguration.cs deleted file mode 100644 index 3f42ae8c2a0..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/GlobalConfiguration.cs +++ /dev/null @@ -1,930 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Management.Automation; -using System.Xml; -using System.Diagnostics.CodeAnalysis; -using System.Security.Principal; -using Microsoft.PowerShell.Commands; -using System.Activities; -using System.Collections.Concurrent; -using Microsoft.PowerShell.Activities; -using System.Management.Automation.Remoting; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// PSWorkflowConfigurationProvider - /// - public class PSWorkflowConfigurationProvider - { - #region Public Methods - - /// - /// Default constructor - /// - public PSWorkflowConfigurationProvider() - { - _wfOptions = new PSWorkflowExecutionOption(); - LoadFromDefaults(); - } - - #endregion - - #region Private Members - - private string _configProviderId; - private String _privateData; - private readonly object _syncObject = new object(); - private bool _isPopulated; - private const string PrivateDataToken = "PrivateData"; - private const string NameToken = "Name"; - private const string ParamToken = "Param"; - private const string ValueToken = "Value"; - - internal const string PSDefaultActivities = "PSDefaultActivities"; - - private PSWorkflowExecutionOption _wfOptions; - - internal const string TokenPersistencePath = "persistencepath"; - internal static readonly string DefaultPersistencePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\Windows\PowerShell\WF\PS"); - - internal const string TokenPersistWithEncryption = "persistwithencryption"; - internal static readonly bool DefaultPersistWithEncryption = false; - - internal const string TokenMaxPersistenceStoreSizeGB = "maxpersistencestoresizegb"; - internal static readonly long DefaultMaxPersistenceStoreSizeGB = 10; // In GB - - internal const string TokenMaxRunningWorkflows = "maxrunningworkflows"; - internal static readonly int DefaultMaxRunningWorkflows = 30; - internal const int MinMaxRunningWorkflows = 1; - internal const int MaxMaxRunningWorkflows = int.MaxValue; - - internal const string TokenAllowedActivity = "allowedactivity"; - internal static readonly IEnumerable DefaultAllowedActivity = new string[] { PSDefaultActivities }; - - internal const string TokenOutOfProcessActivity = "outofprocessactivity"; - internal static readonly IEnumerable DefaultOutOfProcessActivity = new string[] { "InlineScript" }; - - internal const string TokenEnableValidation = "enablevalidation"; - internal static readonly bool DefaultEnableValidation = false; - - internal const string TokenMaxDisconnectedSessions = "maxdisconnectedsessions"; - internal static readonly int DefaultMaxDisconnectedSessions = 1000; - internal const int MinMaxDisconnectedSessions = 1; - internal const int MaxMaxDisconnectedSessions = int.MaxValue; - - internal const string TokenMaxConnectedSessions = "maxconnectedsessions"; - internal static readonly int DefaultMaxConnectedSessions = 100; - internal const int MinMaxConnectedSessions = 1; - internal const int MaxMaxConnectedSessions = int.MaxValue; - - internal const string TokenMaxSessionsPerWorkflow = "maxsessionsperworkflow"; - internal static readonly int DefaultMaxSessionsPerWorkflow = 5; - internal const int MinMaxSessionsPerWorkflow = 1; - internal const int MaxMaxSessionsPerWorkflow = int.MaxValue; - - internal const string TokenMaxSessionsPerRemoteNode = "maxsessionsperremotenode"; - internal static readonly int DefaultMaxSessionsPerRemoteNode = 5; - internal const int MinMaxSessionsPerRemoteNode = 1; - internal const int MaxMaxSessionsPerRemoteNode = int.MaxValue; - - internal const string TokenMaxActivityProcesses = "maxactivityprocesses"; - internal static readonly int DefaultMaxActivityProcesses = 5; - internal const int MinMaxActivityProcesses = 1; - internal const int MaxMaxActivityProcesses = int.MaxValue; - - internal const string TokenActivityProcessIdleTimeoutSec = "activityprocessidletimeoutsec"; - internal static readonly int DefaultActivityProcessIdleTimeoutSec = 60; - internal const int MinActivityProcessIdleTimeoutSec = 1; - internal const int MaxActivityProcessIdleTimeoutSec = int.MaxValue; - - internal const string TokenWorkflowApplicationPersistUnloadTimeoutSec = "workflowapplicationpersistunloadtimeoutsec"; - internal static readonly int DefaultWorkflowApplicationPersistUnloadTimeoutSec = 5; - internal const int MinWorkflowApplicationPersistUnloadTimeoutSec = 0; - internal const int MaxWorkflowApplicationPersistUnloadTimeoutSec = int.MaxValue; - - internal const string TokenWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = "wsmanpluginreportcompletiononzeroactivesessionswaitintervalmsec"; - internal static readonly int DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = 30 * 1000; - internal const int MinWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = 100; - internal const int MaxWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = int.MaxValue; - - internal const string TokenActivitiesCacheCleanupIntervalMSec = "activitiescachecleanupintervalmsec"; - internal static readonly int DefaultActivitiesCacheCleanupIntervalMSec = 5 * 60 * 1000; - internal const int MinActivitiesCacheCleanupIntervalMSec = 100; - internal const int MaxActivitiesCacheCleanupIntervalMSec = int.MaxValue; - - internal const string TokenRemoteNodeSessionIdleTimeoutSec = "remotenodesessionidletimeoutsec"; - internal static readonly int DefaultRemoteNodeSessionIdleTimeoutSec = 60; - internal const int MinRemoteNodeSessionIdleTimeoutSec = 30; - internal const int MaxRemoteNodeSessionIdleTimeoutSec = 30000; - private int _remoteNodeSessionIdleTimeoutSec; - - internal const string TokenSessionThrottleLimit = "sessionthrottlelimit"; - internal static readonly int DefaultSessionThrottleLimit = 100; - internal const int MinSessionThrottleLimit = 1; - internal const int MaxSessionThrottleLimit = int.MaxValue; - - internal const string TokenValidationCacheLimit = "validationcachelimit"; - internal static readonly int DefaultValidationCacheLimit = 10000; - private int _validationCacheLimit; - - internal const string TokenCompiledAssemblyCacheLimit = "compiledassemblycachelimit"; - internal static readonly int DefaultCompiledAssemblyCacheLimit = 10000; - private int _compiledAssemblyCacheLimit; - - internal const string TokenOutOfProcessActivityCacheLimit = "outofprocessactivitycachelimit"; - internal static readonly int DefaultOutOfProcessActivityCacheLimit = 10000; - private int _outOfProcessActivityCacheLimit; - - internal static readonly int DefaultPSPersistInterval = 30; // in seconds - - internal const string TokenWorkflowShutdownTimeoutMSec = "workflowshutdowntimeoutmsec"; - internal static readonly int DefaultWorkflowShutdownTimeoutMSec = 500; // 0.5 secs - internal const int MinWorkflowShutdownTimeoutMSec = 0; - internal const int MaxWorkflowShutdownTimeoutMSec = 5 * 1000; // 5 secs - - internal const string TokenMaxInProcRunspaces = "maxinprocrunspaces"; - internal static readonly int DefaultMaxInProcRunspaces = DefaultMaxRunningWorkflows * 2; - private int _maxInProcRunspaces = DefaultMaxInProcRunspaces; - - private readonly ConcurrentDictionary outOfProcessActivityCache = new ConcurrentDictionary(); - - #endregion Private Members - - #region Private Methods - - internal static PSWorkflowExecutionOption LoadConfig(string privateData, PSWorkflowConfigurationProvider configuration) - { - PSWorkflowExecutionOption target = new PSWorkflowExecutionOption(); - if (String.IsNullOrEmpty(privateData)) - { - return target; - } - - XmlReaderSettings readerSettings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreComments = true, - IgnoreProcessingInstructions = true, - MaxCharactersInDocument = 10000, - XmlResolver = null, - ConformanceLevel = ConformanceLevel.Fragment - }; - - using (XmlReader reader = XmlReader.Create(new StringReader(privateData), readerSettings)) - { - // read the header - if (reader.ReadToFollowing(PrivateDataToken)) - { - HashSet assignedParams = new HashSet(); - bool found = reader.ReadToDescendant(ParamToken); - while (found) - { - if (!reader.MoveToAttribute(NameToken)) - { - throw new PSArgumentException(Resources.NameNotSpecifiedForParam); - } - - string optionName = reader.Value; - - if (!reader.MoveToAttribute(ValueToken)) - { - throw new PSArgumentException(Resources.ValueNotSpecifiedForParam); - } - - if (assignedParams.Contains(optionName.ToLower(CultureInfo.InvariantCulture))) - { - throw new PSArgumentException(Resources.ParamSpecifiedMoreThanOnce, optionName); - } - - string optionValue = reader.Value; - Update(optionName, optionValue, target, configuration); - - assignedParams.Add(optionName.ToLower(CultureInfo.InvariantCulture)); - found = reader.ReadToFollowing(ParamToken); - } - } - } - return target; - } - - /// - /// - /// - public PSWorkflowRuntime Runtime - { - get; - internal set; - } - - private bool? _powerShellActivitiesAreAllowed; - internal bool PSDefaultActivitiesAreAllowed - { - get - { - if (_powerShellActivitiesAreAllowed == null) - { - lock (_syncObject) { - if (_powerShellActivitiesAreAllowed == null) - { - bool allowed = (AllowedActivity ?? new string[0]).Any(a => string.Equals(a, PSDefaultActivities, StringComparison.OrdinalIgnoreCase)); - _powerShellActivitiesAreAllowed = allowed; - } - } - } - return _powerShellActivitiesAreAllowed.Value; - } - } - - /// - /// Using optionName and optionValue updates the current object - /// - /// - /// - /// - /// - private static void Update(string optionName, string value, PSWorkflowExecutionOption target, PSWorkflowConfigurationProvider configuration) - { - switch (optionName.ToLower(CultureInfo.InvariantCulture)) - { - case TokenWorkflowApplicationPersistUnloadTimeoutSec: - target.WorkflowApplicationPersistUnloadTimeoutSec = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec: - target.WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = (int)LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenActivitiesCacheCleanupIntervalMSec: - target.ActivitiesCacheCleanupIntervalMSec = (int)LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenActivityProcessIdleTimeoutSec: - target.ActivityProcessIdleTimeoutSec = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenAllowedActivity: - string[] activities = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - target.AllowedActivity = activities.Select(activity => activity.Trim()).ToArray(); - break; - case TokenEnableValidation: - bool enableValidation; - if (bool.TryParse(value, out enableValidation)) - { - target.EnableValidation = enableValidation; - } - break; - case TokenPersistencePath: - target.PersistencePath = Environment.ExpandEnvironmentVariables(value); - break; - case TokenMaxPersistenceStoreSizeGB: - target.MaxPersistenceStoreSizeGB = (long)LanguagePrimitives.ConvertTo(value, typeof(long), CultureInfo.InvariantCulture); - break; - case TokenPersistWithEncryption: - bool persistWithEncryption; - if (bool.TryParse(value, out persistWithEncryption)) - { - target.PersistWithEncryption = persistWithEncryption; - } - break; - case TokenRemoteNodeSessionIdleTimeoutSec: - target.RemoteNodeSessionIdleTimeoutSec = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - if (configuration != null) - { - configuration._remoteNodeSessionIdleTimeoutSec = target.RemoteNodeSessionIdleTimeoutSec; - } - break; - case TokenMaxActivityProcesses: - target.MaxActivityProcesses = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxConnectedSessions: - target.MaxConnectedSessions = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxDisconnectedSessions: - target.MaxDisconnectedSessions = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxRunningWorkflows: - target.MaxRunningWorkflows = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - configuration._maxInProcRunspaces = target.MaxRunningWorkflows*2; - break; - case TokenMaxSessionsPerRemoteNode: - target.MaxSessionsPerRemoteNode = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxSessionsPerWorkflow: - target.MaxSessionsPerWorkflow = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenOutOfProcessActivity: - string[] outofProcActivities = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - target.OutOfProcessActivity = outofProcActivities.Select(activity => activity.Trim()).ToArray(); - break; - - case TokenSessionThrottleLimit: - { - target.SessionThrottleLimit = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - } - break; - case TokenValidationCacheLimit: - if (configuration != null) - { - configuration._validationCacheLimit = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - } - break; - case TokenCompiledAssemblyCacheLimit: - if (configuration != null) - { - configuration._compiledAssemblyCacheLimit = (int)LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - } - break; - case TokenOutOfProcessActivityCacheLimit: - if (configuration != null) - { - configuration._outOfProcessActivityCacheLimit = (int) LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - } - break; - case TokenWorkflowShutdownTimeoutMSec: - target.WorkflowShutdownTimeoutMSec = (int)LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.InvariantCulture); - break; - case TokenMaxInProcRunspaces: - { - if (configuration != null) - { - configuration._maxInProcRunspaces = - (int) LanguagePrimitives.ConvertTo(value, typeof (int), CultureInfo.InvariantCulture); - } - } - break; - } - } - - #endregion Private Methods - - #region Internal Methods - - /// - /// PSWorkflowConfigurationProvider - /// - /// - /// - public PSWorkflowConfigurationProvider(string applicationPrivateData, string configProviderId) - { - Populate(applicationPrivateData, configProviderId); - } - - private void LoadFromDefaults() - { - _wfOptions.PersistencePath = DefaultPersistencePath; - _wfOptions.MaxPersistenceStoreSizeGB = DefaultMaxPersistenceStoreSizeGB; - _wfOptions.PersistWithEncryption = DefaultPersistWithEncryption; - _wfOptions.MaxRunningWorkflows = DefaultMaxRunningWorkflows; - _wfOptions.AllowedActivity = new List(DefaultAllowedActivity).ToArray(); - _wfOptions.OutOfProcessActivity = new List(DefaultOutOfProcessActivity).ToArray(); - _wfOptions.EnableValidation = DefaultEnableValidation; - _wfOptions.MaxDisconnectedSessions = DefaultMaxDisconnectedSessions; - _wfOptions.MaxConnectedSessions = DefaultMaxConnectedSessions; - _wfOptions.MaxSessionsPerWorkflow = DefaultMaxSessionsPerWorkflow; - _wfOptions.MaxSessionsPerRemoteNode = DefaultMaxSessionsPerRemoteNode; - _wfOptions.MaxActivityProcesses = DefaultMaxActivityProcesses; - _wfOptions.ActivityProcessIdleTimeoutSec = DefaultActivityProcessIdleTimeoutSec; - _wfOptions.WorkflowApplicationPersistUnloadTimeoutSec = DefaultWorkflowApplicationPersistUnloadTimeoutSec; - _wfOptions.WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - _wfOptions.ActivitiesCacheCleanupIntervalMSec = DefaultActivitiesCacheCleanupIntervalMSec; - _wfOptions.RemoteNodeSessionIdleTimeoutSec = DefaultRemoteNodeSessionIdleTimeoutSec; - _wfOptions.SessionThrottleLimit = DefaultSessionThrottleLimit; - _validationCacheLimit = DefaultValidationCacheLimit; - _compiledAssemblyCacheLimit = DefaultCompiledAssemblyCacheLimit; - _outOfProcessActivityCacheLimit = DefaultOutOfProcessActivityCacheLimit; - _wfOptions.WorkflowShutdownTimeoutMSec = DefaultWorkflowShutdownTimeoutMSec; - - ResetCaching(); - } - - private void ResetCaching() - { - _powerShellActivitiesAreAllowed = null; - outOfProcessActivityCache.Clear(); - } - - internal PSSenderInfo _senderInfo = null; - internal void Populate(string applicationPrivateData, string configProviderId, PSSenderInfo senderInfo) - { - _senderInfo = senderInfo; - Populate(applicationPrivateData, configProviderId); - } - - /// - /// Populate the global configuration object with - /// information from the configuration xml - /// - /// private data - /// associated with the endpoint - /// - public void Populate(string applicationPrivateData, string configProviderId) - { - if (_isPopulated) - { - return; - } - - lock (_syncObject) - { - if (!_isPopulated) - { - _privateData = applicationPrivateData; - - string[] tokens = configProviderId.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); - _configProviderId = tokens.Length > 0 ? tokens[tokens.Length - 1] : configProviderId; - _isPopulated = true; - _wfOptions = LoadConfig(_privateData, this); - ResetCaching(); - } - else - { - Debug.Assert(false, "Populate should only be called once"); - } - } - } - - #region PSWorkflowConfigurationProvider Implementation - - /// - /// CreatePSActivityHostController - /// - /// - public virtual PSActivityHostController CreatePSActivityHostController() - { - return new PSOutOfProcessActivityController(Runtime); - } - - /// - /// CreatePSWorkflowInstance - /// - /// - /// - /// - /// - /// - public virtual PSWorkflowInstance CreatePSWorkflowInstance(PSWorkflowDefinition definition, PSWorkflowContext metadata, PSDataCollection pipelineInput, PSWorkflowJob job) - { - return new PSWorkflowApplicationInstance(Runtime, definition, metadata, pipelineInput, job); - } - - /// - /// CreatePSWorkflowInstance - /// - /// - /// - public virtual PSWorkflowInstance CreatePSWorkflowInstance(PSWorkflowId instanceId) - { - return new PSWorkflowApplicationInstance(Runtime, instanceId); - } - - /// - /// CreatePSWorkflowInstanceStore - /// - /// - /// - public virtual PSWorkflowInstanceStore CreatePSWorkflowInstanceStore(PSWorkflowInstance workflowInstance) - { - return new PSWorkflowFileInstanceStore(this, workflowInstance); - } - - /// - /// CreateWorkflowExtensions - /// - /// - public virtual IEnumerable CreateWorkflowExtensions() - { - if (PSWorkflowExtensions.CustomHandler != null) - { - return PSWorkflowExtensions.CustomHandler(); - } - - return null; - } - - /// - /// CreateWorkflowExtensionCreationFunctions - /// - /// - /// - [SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the Workflow.")] - public virtual IEnumerable> CreateWorkflowExtensionCreationFunctions() - { - return null; - } - - /// - /// CreateRemoteRunspaceProvider - /// - /// - public virtual RunspaceProvider CreateRemoteRunspaceProvider() - { - return new ConnectionManager(RemoteNodeSessionIdleTimeoutSec * 1000, - MaxSessionsPerRemoteNode, - SessionThrottleLimit, - MaxConnectedSessions, - MaxDisconnectedSessions); - } - - /// - /// Local runspace provider - /// - /// - /// - public virtual RunspaceProvider CreateLocalRunspaceProvider(bool isUnbounded) - { - if(isUnbounded) - return new LocalRunspaceProvider(RemoteNodeSessionIdleTimeoutSec, LanguageMode); - else - return new LocalRunspaceProvider(RemoteNodeSessionIdleTimeoutSec, MaxInProcRunspaces, LanguageMode); - } - - #endregion - - /// - /// CurrentUserIdentity returns the current user - /// - internal WindowsIdentity CurrentUserIdentity - { - get { return _currentIdentity ?? (_currentIdentity = WindowsIdentity.GetCurrent()); } - } - private WindowsIdentity _currentIdentity; - - /// - /// To be called only by test code - /// - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This method is called from the tests")] - internal void ResetPopulate() - { - lock (_syncObject) - { - _isPopulated = false; - LoadFromDefaults(); - } - } - - #endregion Internal Methods - - #region Configuration Properties - - /// - /// ConfigProviderId - /// - internal string ConfigProviderId - { - get - { - return _configProviderId; - } - } - - /// - /// PersistencePath - /// - internal string PersistencePath - { - get - { - return _wfOptions.PersistencePath; - } - } - - internal bool IsDefaultStorePath; - private string _instanceStorePath; - internal string InstanceStorePath - { - get - { - if (_instanceStorePath != null) - return _instanceStorePath; - - lock (_syncObject) - { - if (_instanceStorePath != null) - return _instanceStorePath; - - if (CurrentUserIdentity.User != null) - { - string path = Path.Combine(_wfOptions.PersistencePath ?? DefaultPersistencePath, _configProviderId ?? "default", CurrentUserIdentity.User.Value); - - WindowsPrincipal principal = new WindowsPrincipal(CurrentUserIdentity); - - bool elevated = false; - bool nonInteractive = false; - bool credssp = false; - - - if (principal.IsInRole(WindowsBuiltInRole.Administrator)) - elevated = true; - - if (PSSessionConfigurationData.IsServerManager == false) // In order to avoid breaking change (SM+ might get affected) - { - if (!principal.IsInRole(new SecurityIdentifier(WellKnownSidType.InteractiveSid, null))) - nonInteractive = true; - - if (_senderInfo != null && - _senderInfo.UserInfo != null && - _senderInfo.UserInfo.Identity != null && - _senderInfo.UserInfo.Identity.AuthenticationType != null && - _senderInfo.UserInfo.Identity.AuthenticationType.Equals("credssp", StringComparison.OrdinalIgnoreCase)) - { - credssp = true; - } - } - - if (credssp) - { - path += "_CP"; // the authentication type is credSSP - } - else - { - if (elevated) - path += "_EL"; // token is elevated - - if (nonInteractive) - path += "_NI"; // token is not interactive - } - - _instanceStorePath = path; - } - IsDefaultStorePath = string.IsNullOrEmpty(_configProviderId) ? true : false; - } - return _instanceStorePath; - } - } - - /// - /// MaxPersistenceStoreSizeGB - /// - internal long MaxPersistenceStoreSizeGB - { - get - { - return _wfOptions.MaxPersistenceStoreSizeGB; - } - } - - /// - /// PersistWithEncryption - /// - internal bool PersistWithEncryption - { - get - { - return _wfOptions.PersistWithEncryption; - } - } - - /// - /// MaxRunningWorkflows - /// - public virtual int MaxRunningWorkflows - { - get - { - return _wfOptions.MaxRunningWorkflows; - } - } - - /// - /// AllowedActivity - /// - public virtual IEnumerable AllowedActivity - { - get - { - return _wfOptions.AllowedActivity; - } - } - - /// - /// OutOfProcActivity - /// - public virtual IEnumerable OutOfProcessActivity - { - get - { - return _wfOptions.OutOfProcessActivity; - } - } - - - /// - /// EnableValidation - /// - public virtual bool EnableValidation - { - get - { - return _wfOptions.EnableValidation; - } - } - - /// - /// MaxDisconnectedSessions - /// - public virtual int MaxDisconnectedSessions - { - get - { - return _wfOptions.MaxDisconnectedSessions; - } - } - - /// - /// MaxConnectedSessions - /// - public virtual int MaxConnectedSessions - { - get - { - return _wfOptions.MaxConnectedSessions; - } - } - - /// - /// MaxSessionsPerWorkflow - /// - internal int MaxSessionsPerWorkflow - { - get - { - return _wfOptions.MaxSessionsPerWorkflow; - } - } - - /// - /// MaxSessionsPerRemoteNode - /// - public virtual int MaxSessionsPerRemoteNode - { - get - { - return _wfOptions.MaxSessionsPerRemoteNode; - } - } - - /// - /// MaxActivityProcesses - /// - public virtual int MaxActivityProcesses - { - get - { - return _wfOptions.MaxActivityProcesses; - } - } - - /// - /// WorkflowApplicationPersistUnloadTimeoutSec - /// - public virtual int PSWorkflowApplicationPersistUnloadTimeoutSec - { - get - { - return _wfOptions.WorkflowApplicationPersistUnloadTimeoutSec; - } - } - - /// - /// WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec - /// - public virtual int WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec - { - get - { - return _wfOptions.WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - } - } - - /// - /// ActivitiesCacheCleanupIntervalMSec - /// - public virtual int ActivitiesCacheCleanupIntervalMSec - { - get - { - return _wfOptions.ActivitiesCacheCleanupIntervalMSec; - } - } - - /// - /// Local Machine Runspace Language Mode - /// - public virtual PSLanguageMode? LanguageMode - { - get - { - return null; - } - } - - /// - /// ActivityProcessIdleTimeOutSec - /// - public virtual int ActivityProcessIdleTimeoutSec - { - get - { - return _wfOptions.ActivityProcessIdleTimeoutSec; - } - } - - /// - /// RemoteNodeSessionIdleTimeOutSec - /// - public virtual int RemoteNodeSessionIdleTimeoutSec - { - get - { - return _wfOptions.RemoteNodeSessionIdleTimeoutSec; - } - } - - /// - /// SessionThrottleLimit - /// - public virtual int SessionThrottleLimit - { - get - { - return _wfOptions.SessionThrottleLimit; - } - } - - internal int ValidationCacheLimit - { - get { return _validationCacheLimit; } - } - - internal int CompiledAssemblyCacheLimit - { - get { return _compiledAssemblyCacheLimit; } - } - - internal int OutOfProcessActivityCacheLimit - { - get - { - return _outOfProcessActivityCacheLimit; - } - } - - /// - /// WorkflowShutdownTimeoutMSec - the maximum time allowed to suspend the workflows before aborting them. - /// - internal int WorkflowShutdownTimeoutMSec - { - get - { - return _wfOptions.WorkflowShutdownTimeoutMSec; - } - } - - /// - /// MaxInProcRunspaces - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public virtual int MaxInProcRunspaces - { - get { return _maxInProcRunspaces; } - } - - /// - /// Get Activity Run Mode InProcess or Out of process - /// - /// - /// - public virtual ActivityRunMode GetActivityRunMode(Activity activity) - { - if (activity == null) - throw new ArgumentNullException("activity"); - - if (outOfProcessActivityCache.Count >= OutOfProcessActivityCacheLimit) - outOfProcessActivityCache.Clear(); - - bool outOfProc = outOfProcessActivityCache.GetOrAdd(activity.GetType(), IsOutOfProcessActivity); - return outOfProc ? ActivityRunMode.OutOfProcess : ActivityRunMode.InProcess; - } - - private bool IsOutOfProcessActivity(Type activityType) - { - return (OutOfProcessActivity ?? new string[0]).Any(outOfProcessActivity => false || IsMatched(outOfProcessActivity, activityType.Name) || IsMatched(outOfProcessActivity, activityType.FullName) || IsMatched(outOfProcessActivity, activityType.Assembly.GetName().Name + "\\" + activityType.Name) || IsMatched(outOfProcessActivity, activityType.Assembly.GetName().Name + "\\" + activityType.FullName) || IsMatched(outOfProcessActivity, activityType.Assembly.GetName().FullName + "\\" + activityType.Name) || IsMatched(outOfProcessActivity, activityType.Assembly.GetName().FullName + "\\" + activityType.FullName)); - } - - private static bool IsMatched(string allowedActivity, string match) - { - return (WildcardPattern.ContainsWildcardCharacters(allowedActivity) - ? new WildcardPattern(allowedActivity, WildcardOptions.IgnoreCase).IsMatch(match) - : string.Equals(allowedActivity, match, StringComparison.OrdinalIgnoreCase)); - } - - #endregion Configuration Properties - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ImportWorkflowCommand.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ImportWorkflowCommand.cs deleted file mode 100644 index 15abf4a655e..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/ImportWorkflowCommand.cs +++ /dev/null @@ -1,1874 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Activities.XamlIntegration; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Management.Automation.Remoting; -using System.Management.Automation.Tracing; -using System.Reflection; -using System.Text; -using System.Xaml; -using Microsoft.PowerShell.Workflow; -using System.Text.RegularExpressions; -using System.Management.Automation.Security; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// A cmdlet to load WF Workflows, expressed as XAML and wrap them - /// in functions. - /// - [Cmdlet(VerbsData.Import, "PSWorkflow", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=210606")] - public class ImportWorkflowCommand : PSCmdlet - { - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static Tracer _structuredTracer = new System.Management.Automation.Tracing.Tracer(); - - /// - /// Paths to the XAML files to load. Wild cards are supported. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] - [Alias("PSPath")] - public String[] Path - { - get - { - return _path; - } - set - { - _path = value; - } - } - private string[] _path; - - /// - /// Paths to the dependent XAML files to load. Wild cards are supported. - /// - [Parameter(Position = 1)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] - [Alias("PSDependentWorkflow")] - public String[] DependentWorkflow - { - get - { - return _dependentWorkflow; - } - set - { - _dependentWorkflow = value; - } - } - private string[] _dependentWorkflow; - - - /// - /// Paths to the dependent XAML files to load. Wild cards are supported. - /// - [Parameter(Position = 2)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] - [Alias("PSDependentAssemblies")] - public String[] DependentAssemblies - { - get - { - return _dependentAssemblies; - } - set - { - _dependentAssemblies = value; - } - } - private string[] _dependentAssemblies; - - /// - /// Flags -force operations - /// - [Parameter] - public SwitchParameter Force - { - get { return _force; } - set { _force = value; } - } - private bool _force; - - /// - /// Process all of the specified XAML files to generate corresponding functions - /// - protected override void ProcessRecord() - { - // In ConstrainedLanguage, XAML workflows are not supported (even from a trusted FullLanguage state), - // unless they are signed in-box OS binaries. - bool checkSignatures = false; - if ((SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) || - (this.SessionState.LanguageMode == PSLanguageMode.ConstrainedLanguage)) - { - // However, this static internal property can be changed by tests that can already run a script - // in full-language mode. - PropertyInfo xamlProperty = typeof(SystemPolicy).GetProperty("XamlWorkflowSupported", BindingFlags.NonPublic | BindingFlags.Static); - checkSignatures = !((bool)xamlProperty.GetValue(null, null)); - } - - string dependentWorkflowAssemblyPath = string.Empty; - List dependentWorkflowContent = new List(); - - if (_dependentWorkflow != null) - { - foreach (string dependentPath in _dependentWorkflow) - { - Collection dependentFilePaths; - try - { - // Try to resolve the pathname, including wildcard resolution - // if an error occurs, generate a non-terminating exception and continue to the next path. - ProviderInfo provider = null; - dependentFilePaths = this.SessionState.Path.GetResolvedProviderPathFromPSPath(dependentPath, /* Ignored */ out provider); - } - catch (ItemNotFoundException notFound) - { - - ErrorRecord er = new ErrorRecord(notFound, "Workflow_XamlFileNotFound", - ErrorCategory.OpenError, dependentPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - - if (dependentFilePaths != null && dependentFilePaths.Count > 0) - { - if (dependentFilePaths.Count == 1 && string.Compare(System.IO.Path.GetExtension(dependentFilePaths[0]), ".dll", StringComparison.OrdinalIgnoreCase) == 0) - { - dependentWorkflowAssemblyPath = dependentFilePaths[0]; - } - else - { - // The argument may have resolved to more than one path. Loop over the resolved paths - // loading each file in turn. If the resolved path does not have a '.xaml' exception, - // generate a non-terminating exception and continue. - foreach (string resolvedPath in dependentFilePaths) - { - string fileText; - //string name; - string extension = System.IO.Path.GetExtension(resolvedPath); - if (string.Compare(extension, ".xaml", StringComparison.OrdinalIgnoreCase) != 0) - { - InvalidOperationException opException = new InvalidOperationException( - string.Format(CultureInfo.CurrentUICulture, Resources.InvalidWorkflowExtension, extension)); - ErrorRecord er = new ErrorRecord(opException, "Workflows_InvalidWorkflowFileExtension", - ErrorCategory.InvalidOperation, resolvedPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - CheckFileSignatureAsNeeded(checkSignatures, resolvedPath); - - try - { - // Finally load the file. If there is an access violation, write a - // not terminating error and continue. - fileText = System.IO.File.ReadAllText(resolvedPath); - dependentWorkflowContent.Add(fileText); - } - catch (System.AccessViolationException noPerms) - { - ErrorRecord er = new ErrorRecord(noPerms, "Workflow_XAMLfileNotAccessible", - ErrorCategory.PermissionDenied, dependentPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - } - } - } - else - { - // If the file spec didn't resolve to a path, write an error - string message = string.Format(CultureInfo.CurrentUICulture, Resources.NoMatchingWorkflowWasFound, dependentPath); - FileNotFoundException fnf = new FileNotFoundException(message); - ErrorRecord er = new ErrorRecord(fnf, "Workflow_NoMatchingWorkflowXamlFileFound", - ErrorCategory.ResourceUnavailable, dependentPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - } - } - } - - Collection filePaths; - - if (_path != null) - { - foreach (string path in _path) - { - try - { - // Try to resolve the pathname, including wildcard resolution - // if an error occurs, generate a non-terminating exception and continue to the next path. - ProviderInfo provider = null; - filePaths = this.SessionState.Path.GetResolvedProviderPathFromPSPath(path, /* Ignored */ out provider); - } - catch (ItemNotFoundException notFound) - { - - ErrorRecord er = new ErrorRecord(notFound, "Workflow_XamlFileNotFound", - ErrorCategory.OpenError, path); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - if (filePaths != null && filePaths.Count > 0) - { - // The argument may have resolved to more than one path. Loop over the resolved paths - // loading each file in turn. If the resolved path does not have a '.xaml' exception, - // generate a non-terminating exception and continue. - foreach (string resolvedPath in filePaths) - { - string fileText = string.Empty; - string name; - string extension = System.IO.Path.GetExtension(resolvedPath); - if (string.Compare(extension, ".xaml", StringComparison.OrdinalIgnoreCase) != 0) - { - InvalidOperationException opException = new InvalidOperationException( - string.Format(CultureInfo.CurrentUICulture, Resources.InvalidWorkflowExtension, extension)); - ErrorRecord er = new ErrorRecord(opException, "Workflows_InvalidWorkflowFileExtension", - ErrorCategory.InvalidOperation, resolvedPath); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - CheckFileSignatureAsNeeded(checkSignatures, resolvedPath); - - FunctionDetails detailsToUseForUpdate = null; - try - { - // Write a message indicating the file name it is trying to load. - string message = string.Format(CultureInfo.CurrentUICulture, Resources.ImportingWorkflowFrom, resolvedPath); - WriteVerbose(message); - - // If -Verbose has been passed to this cmdlet, then the Set operation in GenerateFunctionFromXaml() - // will also be logged the verbose stream. This is desirable as it displays the generated function... - - // Try to read the file. If there is an access violation, write a - // non-terminating error and continue with the next workflow file. - - // check if the function cache already has the entry to this file - // if the xaml was already parsed use the same function definition - // again - name = System.IO.Path.GetFileNameWithoutExtension(resolvedPath); - _structuredTracer.ImportingWorkflowFromXaml(Guid.Empty, name); - FunctionCache.TryGetValue(resolvedPath, out detailsToUseForUpdate); - - if (detailsToUseForUpdate != null && !_force) - { - UpdateFunctionFromXaml(detailsToUseForUpdate); - _structuredTracer.ImportedWorkflowFromXaml(Guid.Empty, name); - continue; - } - - fileText = System.IO.File.ReadAllText(resolvedPath); - } - catch (System.AccessViolationException noPerms) - { - ErrorRecord er = new ErrorRecord(noPerms, "Workflow_XAMLfileNotAccessible", - ErrorCategory.PermissionDenied, path); - WriteError(er); - Tracer.TraceErrorRecord(er); - continue; - } - - Dictionary requiredAssemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); - if (DependentAssemblies != null && DependentAssemblies.Length > 0) - { - foreach (string filePath in DependentAssemblies) - { - requiredAssemblies.Add(System.IO.Path.GetFileNameWithoutExtension(filePath), filePath); - } - } - - FunctionDetails details = GenerateFunctionFromXaml(name, fileText, - requiredAssemblies, - dependentWorkflowContent.ToArray(), - dependentWorkflowAssemblyPath, - resolvedPath, - this.SessionState.LanguageMode); - - // check if the function cache already has the entry to this file - // If detailsToUseForUpdate is not null it is a forced import module - if (detailsToUseForUpdate != null) - { - // Update the cached function details - // - FunctionCache.TryUpdate(resolvedPath, details, detailsToUseForUpdate); - detailsToUseForUpdate = details; - } - else - { - if (FunctionCache.Count == FunctionCacheSize) - FunctionCache.Clear(); - - detailsToUseForUpdate = FunctionCache.GetOrAdd(resolvedPath, details); - } - - UpdateFunctionFromXaml(detailsToUseForUpdate); - - _structuredTracer.ImportedWorkflowFromXaml(Guid.Empty, name); - } - } - else - { - // If the file spec didn't resolve to a path, write an error - string message = string.Format(CultureInfo.CurrentUICulture, Resources.NoMatchingWorkflowWasFound, path); - FileNotFoundException fnf = new FileNotFoundException(message); - ErrorRecord er = new ErrorRecord(fnf, "Workflow_NoMatchingWorkflowXamlFileFound", - ErrorCategory.ResourceUnavailable, path); - WriteError(er); - Tracer.TraceErrorRecord(er); - } - } - } //if - - - } //ProcessRecord - - private static void CheckFileSignatureAsNeeded(bool checkSignatures, string filePath) - { - if (checkSignatures && !System.Management.Automation.Internal.SecuritySupport.IsProductBinary(filePath)) - { - throw new NotSupportedException(Resources.XamlWorkflowsNotSupported); - } - } - - private static readonly ConcurrentDictionary FunctionCache = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - - private const int FunctionCacheSize = 1000; - - /// - /// Load a workflow XAML file from the specified path and generate a PowerShell - /// function from the file. The name of the generated function will be the basename - /// of the XAML file. - /// - /// The name of workflow. - /// The xaml of workflow. - /// - /// Any workflows required by this workflow. - /// Path to the dependent assembly. - /// Resolved Path of the xaml - /// Language mode of source in which workflow should run - private static FunctionDetails GenerateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - string resolvedPath, - PSLanguageMode sourceLanguageMode) - { - if (name == null) - { - ArgumentNullException argNullException = new ArgumentNullException("name"); - Tracer.TraceException(argNullException); - throw argNullException; - } - - string modulePath = System.IO.Path.GetDirectoryName(resolvedPath); - string functionDefinition = CreateFunctionFromXaml( - name, - xaml, - requiredAssemblies, - dependentWorkflows, - dependentAssemblyPath, - null, - modulePath, - false, - "[CmdletBinding()]", - null, /* scriptContent */ - null, /* fullScript */ - null, /* rootWorkflowName */ - sourceLanguageMode, - null); - - FunctionDetails details = new FunctionDetails - {Name = name, FunctionDefinition = functionDefinition, Xaml = xaml}; - - return details; - } - - /// - /// Generate a function in the current session using the specified - /// function details - /// - /// details of the function - private void UpdateFunctionFromXaml(FunctionDetails details) - { - // Bind the command into the caller's command table. Note that if - // ~/ has been passed to this cmdlet, then the Set operation will also be logged - // the verbose stream. This is desirable as it shows the generated function... - string functionName = "function:\\script:" + details.Name; - - // This script block is defined as FullLanguage mode, since it contains - // no text that can be injected by the user. - ScriptBlock xamlFunctionDefinition = null; - PSLanguageMode oldLanguageMode = this.SessionState.LanguageMode; - try - { - this.SessionState.LanguageMode = PSLanguageMode.FullLanguage; - xamlFunctionDefinition = ScriptBlock.Create(details.FunctionDefinition); - } - finally - { - this.SessionState.LanguageMode = oldLanguageMode; - } - - WorkflowInfo workflow = new WorkflowInfo(details.Name, "", xamlFunctionDefinition, details.Xaml, null); - - SessionState.InvokeProvider.Item.Set(functionName, workflow); - } - - /// - /// Executes an instance of the workflow object graph identified by the passed - /// GUID, binding parameters from the Parameters hashtable. - /// - /// The powershell command. - /// The GUID used to identify the workflow to run. - /// The parameters to pass to the workflow instance. - /// The friendly name for the job - /// True if there was a PSParameters collection - /// - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "workFlowDefinition")] - public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync, - bool parameterCollectionProcessed, Hashtable[] parameters) - { - return StartWorkflowApplication(command, jobName, workflowGuid, startAsync, parameterCollectionProcessed, parameters, false); - } - - /// - /// Executes an instance of the workflow object graph identified by the passed - /// GUID, binding parameters from the Parameters hashtable. - /// - /// The powershell command. - /// The GUID used to identify the workflow to run. - /// The parameters to pass to the workflow instance. - /// The friendly name for the job - /// True if there was a PSParameters collection - /// - /// True if debugger is in active state. - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "workFlowDefinition")] - public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync, - bool parameterCollectionProcessed, Hashtable[] parameters, bool debuggerActive) - { - return StartWorkflowApplication(command, jobName, workflowGuid, startAsync, parameterCollectionProcessed, parameters, false, null); - } - - /// - /// Executes an instance of the workflow object graph identified by the passed - /// GUID, binding parameters from the Parameters hashtable. - /// - /// The powershell command. - /// The GUID used to identify the workflow to run. - /// The parameters to pass to the workflow instance. - /// The friendly name for the job - /// True if there was a PSParameters collection - /// - /// True if debugger is in active state. - /// Language mode of source creating workflow. - public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync, - bool parameterCollectionProcessed, Hashtable[] parameters, bool debuggerActive, string SourceLanguageMode) - { - Guid trackingGuid = Guid.NewGuid(); - _structuredTracer.BeginStartWorkflowApplication(trackingGuid); - - if (string.IsNullOrEmpty(workflowGuid)) - { - var exception = new ArgumentNullException("workflowGuid"); - Tracer.TraceException(exception); - throw exception; - } - - if (command == null) - { - var exception = new ArgumentNullException("command"); - Tracer.TraceException(exception); - throw exception; - } - - if (parameterCollectionProcessed) - { - StringBuilder paramString = new StringBuilder(); - StringBuilder computers = new StringBuilder(); - - // Add context for this trace record... - paramString.Append("commandName ='" + command.MyInvocation.MyCommand.Name + "'\n"); - paramString.Append("jobName ='" + jobName + "'\n"); - paramString.Append("workflowGUID = " + workflowGuid + "\n"); - paramString.Append("startAsync " + startAsync.ToString() + "\n"); - - // Stringize the parameter table to look like @{ k1 = v1; k2 = v2; ... } - if (parameters != null) - { - foreach (Hashtable h in parameters) - { - paramString.Append("@{"); - bool first = true; - foreach (DictionaryEntry e in h) - { - if (e.Key != null) - { - if (!first) - { - paramString.Append("'; "); - } - else - { - first = false; - } - paramString.Append(e.Key.ToString()); - paramString.Append("='"); - if (e.Value != null) - { - if (string.Equals(e.Key.ToString(), Constants.ComputerName, StringComparison.OrdinalIgnoreCase)) - { - computers.Append(LanguagePrimitives.ConvertTo(e.Value)); - } - - paramString.Append(e.Value.ToString()); - } - } - } - paramString.Append("}\n "); - } - } - - _structuredTracer.ParameterSplattingWasPerformed(paramString.ToString(), computers.ToString()); - } - - JobDefinition workFlowDefinition = DefinitionCache.Instance.GetDefinition(new Guid(workflowGuid)); - - if (workFlowDefinition == null) - { - var invalidOpException = new InvalidOperationException( - string.Format( - CultureInfo.CurrentUICulture, - Resources.InvalidWorkflowDefinitionState, - DefinitionCache.Instance.CacheSize)); - Tracer.TraceException(invalidOpException); - throw invalidOpException; - } - - ContainerParentJob myJob = null; - - /* - * Iterate through the list of parameters hashtables. Each table may, in turn - * contain a list of computers to target. - */ - var parameterDictionaryList = new List>(); - if (parameters == null || parameters.Length == 0) - { - // There were no parameters so add a dummy parameter collection - // to ensure that at least one child job is created... - parameterDictionaryList.Add(new Dictionary()); - } - else if (parameters.Length == 1 && !parameterCollectionProcessed) - { - // If the parameter collection is just the $PSBoundParameters - // i.e. did not come through the PSParameterCollection list - // then just we can use it as is since it's already been processed through the parameter - // binder... - Hashtable h = parameters[0]; - Dictionary paramDictionary = new Dictionary(); - foreach (var key in parameters[0].Keys) - { - paramDictionary.Add((string)key, h[key]); - } - parameterDictionaryList.Add(paramDictionary); - } - else - { - // Otherwise, iterate through the list of parameter collections using - // a function to apply the parameter binder transformations on each item in the list. - // on the hashtable. - - // Convert the parameters into dictionaries. - foreach (Hashtable h in parameters) - { - if (h != null) - { - Dictionary canonicalEntry = ConvertToParameterDictionary(h); - - // Add parameter dictionary to the list... - parameterDictionaryList.Add(canonicalEntry); - } - } - } - - JobInvocationInfo specification = new JobInvocationInfo(workFlowDefinition, parameterDictionaryList); - specification.Name = jobName; - - specification.Command = command.MyInvocation.InvocationName; - _structuredTracer.BeginCreateNewJob(trackingGuid); - myJob = command.JobManager.NewJob(specification) as ContainerParentJob; - _structuredTracer.EndCreateNewJob(trackingGuid); - - // Pass the source language mode to the workflow job so that it can be - // applied during activity execution. - PSLanguageMode sourceLanguageModeValue; - PSLanguageMode? sourceLanguageMode = null; - if (!string.IsNullOrEmpty(SourceLanguageMode) && Enum.TryParse(SourceLanguageMode, out sourceLanguageModeValue)) - { - sourceLanguageMode = sourceLanguageModeValue; - } - - // Raise engine event of new WF job start for debugger, if - // debugger is active (i.e., has breakpoints set). - if (debuggerActive) - { - RaiseWFJobEvent(command, myJob, startAsync); - } - - if (startAsync) - { - foreach(PSWorkflowJob childjob in myJob.ChildJobs) - { - if (!PSSessionConfigurationData.IsServerManager) - { - childjob.EnableStreamUnloadOnPersistentState(); - } - childjob.SourceLanguageMode = sourceLanguageMode; - } - myJob.StartJobAsync(); - } - else - { - // If the job is running synchronously, the PSWorkflowJob(s) need to know - // so that errors are decorated correctly. - foreach (PSWorkflowJob childJob in myJob.ChildJobs) - { - childJob.SynchronousExecution = true; - childJob.SourceLanguageMode = sourceLanguageMode; - } - myJob.StartJob(); - } - - // write an event specifying that job creation is done - _structuredTracer.EndStartWorkflowApplication(trackingGuid); - _structuredTracer.TrackingGuidContainerParentJobCorrelation(trackingGuid, myJob.InstanceId); - - return myJob; - } - - private static void RaiseWFJobEvent(PSCmdlet command, ContainerParentJob job, bool startAsync) - { - var eventManager = command.Events; - Debug.Assert(eventManager != null, "Event Manager cannot be null."); - - foreach (PSWorkflowJob cJob in job.ChildJobs) - { - if (cJob == null) continue; - - // Raise event for each child job. - eventManager.GenerateEvent( - sourceIdentifier: PSEngineEvent.WorkflowJobStartEvent, - sender: null, - args: new object[] { new PSJobStartEventArgs(cJob, cJob.PSWorkflowDebugger, startAsync) }, - extraData: null, - processInCurrentThread: true, - waitForCompletionInCurrentThread: true); - } - } - - private static Dictionary ConvertToParameterDictionary(Hashtable h) - { - if (h == null) - return null; - - Dictionary paramDictionary = new Dictionary(); - - foreach (var key in h.Keys) - { - paramDictionary.Add((string)key, h[key]); - } - return paramDictionary; - } - - /// - /// Retrieve a localized error message saying that only a single default parameter collection can be specified - /// - public static string ParameterErrorMessage - { - get - { - return Resources.OnlyOneDefaultParameterCollectionAllowed; - } - } - - /// - /// Retrieve a localized error message saying that AsJob, JobName and PSParameterCollection cannot be specified as entries to PSParameterCollection - /// - public static string InvalidPSParameterCollectionEntryErrorMessage - { - get - { - return Resources.AsJobAndJobNameNotAllowed; - } - } - - /// - /// Retrieve a localized error message saying that the only AsJob, JobName and InputObject can be used outside of PSParameterCollection. - /// - public static string InvalidPSParameterCollectionAdditionalErrorMessage - { - get - { - return Resources.ParameterCollectionOnlyUsedWithAsJobAndJobName; - } - } - - /// - /// Retrieve a localized error message saying that starting the workflow failed... - /// - public static string UnableToStartWorkflowMessageMessage - { - get - { - return Resources.UnableToStartWorkflow; - } - } - - /// - /// Convert the Xaml based workflow into object-graph activity. - /// - /// Xaml representing workflow. - /// Activity representing the workflow. - internal static Activity ConvertXamlToActivity(string xaml) - { - Tracer.WriteMessage("Trying to convert Xaml into Activity."); - - if (string.IsNullOrEmpty(xaml)) - { - // Tracer* _M3PErrorImportingWorkflowFromXaml - ArgumentNullException exception = new ArgumentNullException("xaml", Resources.XamlNotNull); - Tracer.TraceException(exception); - throw exception; - } - - StringReader sReader = new StringReader(xaml); - Activity workflow; - - try - { - workflow = ActivityXamlServices.Load(sReader); - } - finally - { - sReader.Dispose(); - } - //_M3PImportedWorkflowFromXaml - - return workflow; - } - - internal static ConcurrentDictionary compiledAssemblyCache = new ConcurrentDictionary(); - - internal static string CompileDependentWorkflowsToAssembly(string[] dependentWorkflows, Dictionary requiredAssemblies) - { - Debug.Assert(dependentWorkflows.Length > 0, "There should be atleast one dependent workflow."); - Debug.Assert((PSWorkflowRuntime.Instance.Configuration).GetType() == typeof(PSWorkflowConfigurationProvider), "type mismatch error."); - - PSWorkflowConfigurationProvider config = (PSWorkflowConfigurationProvider)PSWorkflowRuntime.Instance.Configuration; - - // Calculating Hashcode - string hashcode = string.Empty; - - List codes = new List(); - foreach (string dependentWorkflow in dependentWorkflows) - { - codes.Add(dependentWorkflow.GetHashCode()); - } - codes.Sort(); - - foreach (int code in codes) - { - hashcode += code.ToString(CultureInfo.InvariantCulture); - } - - Debug.Assert(string.IsNullOrEmpty(hashcode) == false, "Hash code should not be null or empty"); - - WorkflowRuntimeCompilation compiler = null; - - // check if the hashcode already exist in cache - if (compiledAssemblyCache.ContainsKey(hashcode)) - { - compiledAssemblyCache.TryGetValue(hashcode, out compiler); - } - - if (compiler == null) - { - try - { - compiler = new WorkflowRuntimeCompilation(); - - compiler.Compile(new List(dependentWorkflows), requiredAssemblies); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - // sanity check to block the unbounded increase in the cache size - if (compiledAssemblyCache.Keys.Count >= config.CompiledAssemblyCacheLimit) - { - compiledAssemblyCache.Clear(); - } - - compiledAssemblyCache.TryAdd(hashcode, compiler); - } - - // Throw an error if there was a compilation problem - if (!compiler.BuildReturnedCode || !File.Exists(compiler.AssemblyPath)) - { - string message = string.Format(CultureInfo.CurrentUICulture, - Resources.CompilationErrorWhileBuildingWorkflows, - compiler.BuildLogPath); - throw new InvalidDataException(message); - } - - return compiler.AssemblyPath; - } - - /// - /// Convert the Xaml based workflow into object-graph activity with additional xamls assembly provided. - /// - /// Xaml representing workflow. - /// Any workflows required by this workflow. - /// - /// The path to the compiled assembly for any dependent workflows. - /// The compiled assembly. - /// The compiled assembly name. - /// Activity representing the workflow. - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", - MessageId = "System.Reflection.Assembly.LoadFrom")] - internal static Activity ConvertXamlToActivity(string xaml, string[] dependentWorkflows, - Dictionary requiredAssemblies, - ref string compiledAssemblyPath, ref Assembly compiledAssembly, - ref string compiledAssemblyName) - { - _structuredTracer.ImportedWorkflowFromXaml(Guid.Empty, string.Empty); - Tracer.WriteMessage("Trying to convert Xaml into Activity and using additional xamls assembly."); - - if (string.IsNullOrEmpty(xaml)) - { - ArgumentNullException exception = new ArgumentNullException("xaml", Resources.XamlNotNull); - Tracer.TraceException(exception); - _structuredTracer.ErrorImportingWorkflowFromXaml(Guid.Empty, exception.Message); - throw exception; - } - - Activity workflow; - XamlXmlReaderSettings readerSettings = new XamlXmlReaderSettings(); - - // If they passed in an assembly path, use it - if (!String.IsNullOrEmpty(compiledAssemblyPath)) - { - if (compiledAssembly == null && String.IsNullOrEmpty(compiledAssemblyName)) - { - compiledAssembly = Assembly.LoadFrom(compiledAssemblyPath); - compiledAssemblyName = compiledAssembly.GetName().Name; - } - - readerSettings.LocalAssembly = compiledAssembly; - } - else if ((dependentWorkflows != null) && (dependentWorkflows.Length > 0)) - { - // Otherwise, it's a first-time compilation - string assemblyPath = CompileDependentWorkflowsToAssembly(dependentWorkflows, requiredAssemblies); - readerSettings.LocalAssembly = Assembly.LoadFrom(assemblyPath); - compiledAssemblyPath = assemblyPath; - compiledAssembly = Assembly.LoadFrom(compiledAssemblyPath); - compiledAssemblyName = compiledAssembly.GetName().Name; - - readerSettings.LocalAssembly = compiledAssembly; - } - - using (StringReader sReader = new StringReader(xaml)) - { - XamlXmlReader reader = new XamlXmlReader(sReader, readerSettings); - workflow = ActivityXamlServices.Load(reader); - } - - _structuredTracer.ImportedWorkflowFromXaml(Guid.Empty, string.Empty); - - return workflow; - } - - private class FunctionDetails - { - internal string Name { get; set; } - internal string FunctionDefinition { get; set; } - internal string Xaml { get; set; } - } - - #region Moved from AstToWorkflowConverter - - private const string functionNamePattern = "^[a-zA-Z0-9-_]*$"; - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes - ) - { - return CreateFunctionFromXaml( - name, - xaml, - requiredAssemblies, - dependentWorkflows, - dependentAssemblyPath, - parameterValidation, - modulePath, - scriptWorkflow, - workflowAttributes, - null); - } - - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// File path containing script content. - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes, - string scriptContent - ) - { - return CreateFunctionFromXaml( - name, - xaml, - requiredAssemblies, - dependentWorkflows, - dependentAssemblyPath, - parameterValidation, - modulePath, - scriptWorkflow, - workflowAttributes, - scriptContent, - null); - } - - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// File path containing script content. - /// Full source script. - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes, - string scriptContent, - string fullScript - ) - { - return CreateFunctionFromXaml(name, xaml, requiredAssemblies, dependentWorkflows, dependentAssemblyPath, - parameterValidation, modulePath, scriptWorkflow, workflowAttributes, scriptContent, fullScript, null); - } - - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// File path containing script content. - /// Full source script. - /// Only root Workflow will be compiled - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes, - string scriptContent, - string fullScript, - string rootWorkflowName - ) - { - return CreateFunctionFromXaml(name, xaml, requiredAssemblies, dependentWorkflows, dependentAssemblyPath, parameterValidation, modulePath, - scriptWorkflow, workflowAttributes, scriptContent, fullScript, rootWorkflowName, null, null); - } - - /// - /// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will - /// run the workflow. - /// - /// Workflow name - /// Workflow Xaml definition - /// Required assemblies - /// Dependent workflows - /// Path for dependent assemblies - /// Workflow parameters - /// Module path - /// True if this is script workflow - /// the attribute string to use for the workflow, should be '[CmdletBinding()]' - /// File path containing script content. - /// Full source script. - /// Only root Workflow will be compiled - /// Language mode of source that is creating the workflow - /// Optional collection of parameter attribute Asts - /// - public static string CreateFunctionFromXaml( - string name, - string xaml, - Dictionary requiredAssemblies, - string[] dependentWorkflows, - string dependentAssemblyPath, - Dictionary parameterValidation, - string modulePath, - bool scriptWorkflow, - string workflowAttributes, - string scriptContent, - string fullScript, - string rootWorkflowName, - PSLanguageMode? sourceLanguageMode, - ReadOnlyCollection attributeAstCollection - ) - { - // check to see if the specified name is allowed - if (!Regex.IsMatch(name, functionNamePattern)) - { - throw new PSArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.WorkflowNameInvalid, name)); - } - - WorkflowJobDefinition definition = null; - Activity workflow = null; - - if (scriptWorkflow) - { - workflow = DefinitionCache.Instance.GetActivityFromCache(xaml, out definition); - } - - if (workflow == null) - { - definition = new WorkflowJobDefinition(typeof(WorkflowJobSourceAdapter), name, null, modulePath, dependentWorkflows, dependentAssemblyPath, requiredAssemblies, xaml); - definition.IsScriptWorkflow = scriptWorkflow; - bool windowsWorkflow; - workflow = DefinitionCache.Instance.CompileActivityAndSaveInCache(definition, null, requiredAssemblies, - out windowsWorkflow, rootWorkflowName); - } - - definition.WorkflowScriptFile = scriptContent; - definition.WorkflowFullScript = fullScript; - - // this can throw exceptions if the xaml is malformed. - DynamicActivity daBody = workflow as DynamicActivity; - - StringBuilder innerParamDefinitions = new StringBuilder(); - StringBuilder outerParamDefinitions = new StringBuilder(); - string workflowGuid = definition.InstanceId.ToString(); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "Generating function for name: {0}, WFGuid: {1}", name, workflowGuid)); - - // String to hold any updates we do for parameter defaults - List parametersWithDefaults = new List(); - string defaultUpdates = ""; - - // If the workflow is a DynamicActivity, we can use the Properties - // property to retrieve parameters to the workflow and synthesize - // PowerShell parameter declarations. - if (daBody != null) - { - foreach (var p in daBody.Properties) - { - // Skip out arguments - if (typeof(System.Activities.OutArgument).IsAssignableFrom(p.Type)) - { - continue; - } - - // If the parameter name is one of the expected collisions, don't add it to the list. - if (p.Name.Equals(Constants.ComputerName, StringComparison.OrdinalIgnoreCase) || p.Name.Equals(Constants.PrivateMetadata, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (p.Name.Equals("InputObject", StringComparison.OrdinalIgnoreCase) || p.Name.Equals("AsJob", StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - string paramTypeName = (string)LanguagePrimitives.ConvertTo(p.Type.GetGenericArguments()[0], - typeof(string), CultureInfo.InvariantCulture); - - string mandatory = ""; - string parameterDefault = ""; - - // If we got specific validations to add, add those - // We only get these in script-based workflow case, but not in XAML case. - if ((parameterValidation != null) && parameterValidation.ContainsKey(p.Name)) - { - ParameterAst parameter = parameterValidation[p.Name]; - - foreach (AttributeBaseAst attribute in parameter.Attributes) - { - innerParamDefinitions.Append(attribute.ToString()); - innerParamDefinitions.Append("\n "); - - var attributeAst = attribute as AttributeAst; - if (attributeAst == null || !string.Equals(attribute.TypeName.Name, "Parameter", StringComparison.OrdinalIgnoreCase)) - { - // If we have a Credential Attribute, it has been added to the inner function, it does not need to be added to the outer definition. - // This will prevent prompting for the cred twice. - if (!string.Equals(attribute.TypeName.FullName, "System.Management.Automation.CredentialAttribute", StringComparison.OrdinalIgnoreCase)) - { - outerParamDefinitions.Append(attribute.ToString()); - outerParamDefinitions.Append("\n "); - } - - continue; - } - - string updatedAttribute = "[Parameter("; - bool first = true; - foreach (var namedAttribute in attributeAst.NamedArguments) - { - if (string.Equals(namedAttribute.ArgumentName, "Mandatory", - StringComparison.OrdinalIgnoreCase)) - continue; - if (string.Equals(namedAttribute.ArgumentName, "ValueFromPipeline", StringComparison.OrdinalIgnoreCase) - && string.Equals(namedAttribute.Argument.Extent.Text, "$true", StringComparison.OrdinalIgnoreCase)) - { - throw new PSInvalidOperationException(Resources.ValueFromPipelineNotSupported); - } - - if (!first) updatedAttribute += ","; - first = false; - updatedAttribute += namedAttribute.ToString(); - } - updatedAttribute += ")]"; - outerParamDefinitions.Append(updatedAttribute); - outerParamDefinitions.Append("\n "); - } - - if (parameter.DefaultValue != null) - { - parameterDefault = " = " + parameter.DefaultValue.ToString(); - parametersWithDefaults.Add("'" + p.Name + "'"); - } - } - // Otherwise, add our default treatment - // XAML workflows only go through this code path. - // Scriptworkflow ALSO comes through this path. - else - { - // If the parameter is an In parameter with the RequiredArgument attribute then make it mandatory - if (typeof(System.Activities.InArgument).IsAssignableFrom(p.Type)) - { - if (p.Attributes != null) - { - foreach (var attribute in p.Attributes) - { - // Check the type of the attribute - if (attribute.TypeId.GetType() == typeof(RequiredArgumentAttribute)) - { - mandatory = "[Parameter(Mandatory=$true)] "; - } - } - } - } - } - - innerParamDefinitions.Append(String.Format(CultureInfo.InvariantCulture, - "{0}[{1}] ${2}{3},\n ", - mandatory, paramTypeName, p.Name, parameterDefault)); - outerParamDefinitions.Append(String.Format(CultureInfo.InvariantCulture, - "[{0}] ${1}{2},\n ", - paramTypeName, p.Name, parameterDefault)); - } - - if (parametersWithDefaults.Count > 0) - { - defaultUpdates = @" - # Update any parameters that had default values in the workflow - $parametersWithDefaults = @(" + String.Join(", ", parametersWithDefaults.ToArray()) + ")\n"; - } - else - { - defaultUpdates = @" - # None of the workflow parameters had default values - $parametersWithDefaults = @()"; - } - - // Add code into the function to handle PSParameterCollection parameter if present. - defaultUpdates += @" - trap { break } - $parameterCollectionProcessed = $false - $PSParameterCollectionDefaultsMember = $null - $suspendOnError = $false - - if ($PSBoundParameters.ContainsKey('PSParameterCollection')) - { - # validate parameters used with PSParameterCollection - foreach ($pa in $PSBoundParameters.Keys) - { - if ($pa -eq 'JobName' -or $pa -eq 'AsJob' -or $pa -eq 'InputObject' -or $pa -eq 'PSParameterCollection') - { - continue - } - $msg = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::InvalidPSParameterCollectionAdditionalErrorMessage; - throw (New-Object System.Management.Automation.ErrorRecord $msg, StartWorkflow.InvalidArgument, InvalidArgument, $PSParameterCollection) - } - $parameterCollectionProcessed = $true - - # See if there is a defaults collection, indicated by '*' - foreach ($collection in $PSParameterCollection) - { - if ($collection['" + Constants.ComputerName + @"'] -eq '*' ) - { - if ($null -ne $PSParameterCollectionDefaultsMember) - { - $msg = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::ParameterErrorMessage; - throw ( New-Object System.Management.Automation.ErrorRecord $msg, StartWorkflow.InvalidArgument, InvalidArgument, $PSParameterCollection) - } - $PSParameterCollectionDefaultsMember = $collection; - foreach($parameter in $parametersWithDefaults) - { - if(! $collection.ContainsKey($parameter)) - { - $collection[$parameter] = (Get-Variable $parameter).Value - } - } - } - } - - $PSParameterCollection = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::MergeParameterCollection( - $PSParameterCollection, $PSParameterCollectionDefaultsMember) - - # canonicalize each collection... - $PSParameterCollection = foreach ( $c in $PSParameterCollection) { - if($c.containskey('AsJob') -or $c.containsKey('JobName') -or $c.containsKey('PSParameterCollection') -or $c.containsKey('InputObject')) - { - $msg = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::InvalidPSParameterCollectionEntryErrorMessage; - throw ( New-Object System.Management.Automation.ErrorRecord $msg, StartWorkflow.InvalidArgument, InvalidArgument, $PSParameterCollection) - } - - if ($c['" + Constants.ErrorAction + @"'] -eq ""Suspend"") - { - $suspendOnError = $true - $c['" + Constants.ErrorAction + @"'] = ""Continue"" - } - - $validated = & """ + name + @""" @c - $validated['" + Constants.PSSuspendOnError + @"'] = $suspendOnError - $validated - } - - # If there was no '*' collection, added the parameter defaults - # to each individual collection if the parameter isn't already there... - if (-not $PSParameterCollectionDefaultsMember) - { - foreach ($collection in $PSParameterCollection) - { - foreach($parameter in $parametersWithDefaults) - { - if(! $collection.ContainsKey($parameter)) - { - $collection[$parameter] = (Get-Variable $parameter).Value - } - } - } - } - } - else - { - if ($PSBoundParameters['" + Constants.ErrorAction + @"'] -eq ""Suspend"") - { - $errorActionPreference = ""Continue"" - $suspendOnError = $true - $PSBoundParameters['" + Constants.ErrorAction + @"'] = ""Continue"" - } - - $PSBoundParameters = & """ + name + @""" @PSBoundParameters - - # no PSParameterCollection so add the default values to PSBoundParameters - foreach($parameter in $parametersWithDefaults) - { - if(! $PSBoundParameters.ContainsKey($parameter)) - { - $PSBoundParameters[$parameter] = (Get-Variable $parameter).Value - } - } - } - "; - } - - // Escaping the single quotes from the module path - modulePath = CodeGeneration.EscapeSingleQuotedStringContent(modulePath); - - //Generate the PowerShell function template - string functionPrefixTemplate = AddCommonWfParameters(false, workflowAttributes); - string validationFunctionPrefixTemplate = AddCommonWfParameters(true, workflowAttributes); - - string outerParamDefinitionsString = outerParamDefinitions.ToString(); - outerParamDefinitionsString = CodeGeneration.EscapeFormatStringContent(outerParamDefinitionsString); - string functionPrefix = String.Format(CultureInfo.InvariantCulture, functionPrefixTemplate, outerParamDefinitionsString); - - // Generate the param block for the synthesized function - string innerParamDefinitionsString = innerParamDefinitions.ToString(); - innerParamDefinitionsString = CodeGeneration.EscapeFormatStringContent(innerParamDefinitionsString); - // For the parameter validation function, add an extra param definition for $PSInputCollection - string validationFunctionPrefix = String.Format(CultureInfo.InvariantCulture, validationFunctionPrefixTemplate, innerParamDefinitionsString + "$PSInputCollection"); - - StringBuilder completeFunctionDefinitionTemplate = new StringBuilder(); - completeFunctionDefinitionTemplate.AppendLine(functionPrefix); - completeFunctionDefinitionTemplate.AppendLine(" begin {{"); - completeFunctionDefinitionTemplate.AppendLine(" function " + name + " {{"); - completeFunctionDefinitionTemplate.AppendLine(validationFunctionPrefix); - completeFunctionDefinitionTemplate.AppendLine(" $PSBoundParameters"); - completeFunctionDefinitionTemplate.AppendLine(" }}"); - completeFunctionDefinitionTemplate.AppendLine(FunctionBodyTemplate); - - // Mark the function definition with sourceLanguageMode (language mode that function can run under, i.e., - // as trusted or not trusted), unless the workflow script is marked with the "SecurityCritical" attribute in - // which case the function will always be run under the current system lock down setting. - bool isSecurityCritical = ContainsSecurityCriticalAttribute(attributeAstCollection); - string sourceLanguageModeStr = (!isSecurityCritical && (sourceLanguageMode != null)) ? sourceLanguageMode.ToString() : string.Empty; - - // Combine the pieces to create the complete function - string functionDefinition = String.Format(CultureInfo.InvariantCulture, completeFunctionDefinitionTemplate.ToString(), - defaultUpdates, workflowGuid, modulePath, sourceLanguageModeStr); - -#if DEBUG - // Verify that the generated function is valid powershell. This is only an issue when changing the - // generation code so it's debug only... - Collection templateErrors = null; - System.Management.Automation.PSParser.Tokenize(functionDefinition, out templateErrors); - if (templateErrors != null && templateErrors.Count > 0) - { - StringBuilder message = new StringBuilder(); - foreach (PSParseError parseErr in templateErrors) - { - message.Append(parseErr.Token.Content).Append(':').Append(parseErr.Message).Append("\n"); - } - message.Append("`nFunction code:`n").Append(functionDefinition); - throw new InvalidOperationException(message.ToString()); - } -#endif - workflow = null; - daBody = null; - - // strip the comments in fre build -#if !DEBUG - functionDefinition = System.Text.RegularExpressions.Regex.Replace(functionDefinition, "^ *\\#.*$", "", RegexOptions.Multiline); -#endif - return functionDefinition; - } - - private static Type securityCriticalAttributeType = typeof(System.Security.SecurityCriticalAttribute); - private static bool ContainsSecurityCriticalAttribute(ReadOnlyCollection attributeAsts) - { - if (attributeAsts != null) - { - foreach (var attributeAst in attributeAsts) - { - if (attributeAst.TypeName.GetReflectionAttributeType() == securityCriticalAttributeType) - { - return true; - } - } - } - - return false; - } - - - /// - /// - /// - /// - /// - /// - public static Hashtable[] MergeParameterCollection(Hashtable[] parameterCollection, Hashtable defaultsParameterCollection) - { - if (defaultsParameterCollection == null) return parameterCollection; - - List updatedParameterCollection = new List(); - foreach (var collection in parameterCollection) - { - if (collection.ContainsKey(Constants.ComputerName)) - { - var cnobject = collection[Constants.ComputerName]; - var pso = cnobject as PSObject; - string name; - if (pso != null) - name = pso.BaseObject as string; - else - name = cnobject as string; - if (name != null && name.Equals("*")) continue; - } - foreach (var parameterName in defaultsParameterCollection.Keys) - { - if (parameterName.Equals(Constants.ComputerName)) continue; - if (collection.ContainsKey(parameterName)) continue; - collection.Add(parameterName, defaultsParameterCollection[parameterName]); - } - - updatedParameterCollection.Add(collection); - } - return updatedParameterCollection.ToArray(); - } - - /// - /// Adds common workflow parameters - /// - /// - /// - /// - internal static string AddCommonWfParameters(bool innerfunction, string workflowAttributes) - { - // If changes are made to the common workflow parameters, then Intellisense will need to be updated as well - // as the strings are hard coded, see admin\monad\src\engine\CommandCompletion\PseudoParameterBinder.cs. - string[] commonParameters = new string[] - { - "[string[]] $" + Constants.ComputerName, - "[ValidateNotNullOrEmpty()] $" + Constants.Credential, - "[uint32] $" + Constants.ConnectionRetryCount, - "[uint32] $" + Constants.ConnectionRetryIntervalSec, - "[ValidateRange(1, " + Constants.Int32MaxValueDivideByThousand + ")][uint32] $" + Constants.PSRunningTime, - "[ValidateRange(1, " + Constants.Int32MaxValueDivideByThousand + ")][uint32] $" + Constants.PSElapsedTime, - "[bool] $" + Constants.Persist, - "[ValidateNotNullOrEmpty()] [System.Management.Automation.Runspaces.AuthenticationMechanism] $" + Constants.Authentication, - "[ValidateNotNullOrEmpty()][System.Management.AuthenticationLevel] $" + Constants.AuthenticationLevel, - "[ValidateNotNullOrEmpty()] [string] $" + Constants.ApplicationName, - "[uint32] $" + Constants.Port, - "[switch] $" + Constants.UseSSL, - "[ValidateNotNullOrEmpty()] [string] $" + Constants.ConfigurationName, - "[ValidateNotNullOrEmpty()][string[]] $" + Constants.ConnectionURI, - "[switch] $" + Constants.AllowRedirection, - "[ValidateNotNullOrEmpty()][System.Management.Automation.Remoting.PSSessionOption] $" + Constants.SessionOption, - "[ValidateNotNullOrEmpty()] [string] $" + Constants.CertificateThumbprint, - "[hashtable] $" + Constants.PrivateMetadata, - "[switch] $" + Constants.AsJob, - "[string] $" + Constants.JobName, - "$InputObject" - }; - - string functionPrefix = ""; - foreach (string workflowAttribute in workflowAttributes.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)) - { - functionPrefix += - @" - " + CodeGeneration.EscapeFormatStringContent(workflowAttribute); - } - - functionPrefix += - @" - param ( - {0}"; - - // For inner function, don't add -PSParameterCollection param. AsJob and JobName are needed in the inner function for - // parameter validation in non-PSParameterCollection cases. For PSParameterCollection, they will be checked for - // with a separate check. - // We want it at workflow function, but not in the subfunction that validates the parameter for PSParameterCollection - if (!innerfunction) - { - functionPrefix += - @" - [hashtable[]] $" + Constants.PSParameterCollection; - } - - //Add all the parametersets to each of the common WF parameter - foreach (string commonParameter in commonParameters) - { - if (commonParameter.Equals("$InputObject")) - { - functionPrefix += @", - " + "[Parameter(ValueFromPipeline=$true)]" + commonParameter; - } - else - { - functionPrefix += @", - " + commonParameter; - } - } - - //Close the param block - functionPrefix += @" - )"; - - return functionPrefix; - } - - const string FunctionBodyTemplate = @" - - $PSInputCollection = New-Object 'System.Collections.Generic.List[PSObject]' - }} - - process {{ - if ($PSBoundParameters.ContainsKey('InputObject')) - {{ - $PSInputCollection.Add($InputObject) - }} - }} - - end {{ - - {0} - if ($PSBoundParameters['" + Constants.Credential + @"']) - {{ - $CredentialTransform = New-Object System.Management.Automation.CredentialAttribute - $LocalCredential = $CredentialTransform.Transform($ExecutionContext, $PSCredential) - $PSBoundParameters['PSCredential'] = [system.management.automation.pscredential]$LocalCredential - - if (!$PSBoundParameters['" + Constants.ComputerName + @"'] -and !$PSBoundParameters['" + Constants.ConnectionURI + @"']) - {{ - $PSBoundParameters['" + Constants.ComputerName + @"'] = New-Object string @(,'localhost') - }} - }} - - # Extract the job name if specified - $jobName = '' - if ($PSBoundParameters['" + Constants.JobName + @"']) - {{ - $jobName = $PSBoundParameters['" + Constants.JobName + @"'] - [void] $PSBoundParameters.Remove('" + Constants.JobName + @"'); - }} - - # Extract the PSParameterCollection if specified - [hashtable[]] $jobSpecifications = @() - $parametersCollection = $null; - if ($PSBoundParameters['" + Constants.PSParameterCollection + @"']) - {{ - $parameterSCollection = $PSBoundParameters['" + Constants.PSParameterCollection + @"'] - [void] $PSBoundParameters.Remove('" + Constants.PSParameterCollection + @"'); - }} - - # Remove the InputObject parameter from the bound parameters collection - if ($PSBoundParameters['" + Constants.InputObject + @"']) - {{ - [void] $PSBoundParameters.Remove('" + Constants.InputObject + @"'); - }} - - # Remove parameters consumed by this function or PowerShell itself - $null = $PSBoundParameters.Remove('" + Constants.AsJob + @"') - $null = $psBoundParameters.Remove('" + Constants.WarningVariable + @"') - $null = $psBoundParameters.Remove('" + Constants.ErrorVariable + @"') - $null = $psBoundParameters.Remove('" + Constants.OutVariable + @"') - $null = $psBoundParameters.Remove('" + Constants.OutBuffer + @"') - $null = $psBoundParameters.Remove('" + Constants.PipelineVariable + @"') - $null = $psBoundParameters.Remove('" + Constants.InformationVariable + @"') - - # Add parameter to add the path of the workflow module, needed by Import-LocalizedData - # which uses this as a base path to find localized content files. - $psBoundParameters['" + Constants.ModulePath + @"'] = '{2}' - - # Variable that contains the source language mode. - [string] $SourceLanguageMode = '{3}' - - if (Test-Path variable:\PSSenderInfo) - {{ - $psBoundParameters['" + Constants.PSSenderInfo + @"'] = $PSSenderInfo - }} - - $psBoundParameters['" + Constants.PSCurrentDirectory + @"'] = $pwd.Path - $psBoundParameters['" + Constants.PSSuspendOnError + @"'] = $suspendOnError - - # Process author-specified metadata which is set using - # the Private member in the module manifest - $myCommand = $MyInvocation.MyCommand - $myModule = $myCommand.Module - if ($myModule) - {{ - # The function was defined in a module so look for - # the PrivateData member - [Hashtable] $privateData = $myModule.PrivateData -as [Hashtable] - - if ($privateData) - {{ - # Extract the nested hashtable corresponding to this - # command - [hashtable] $authorMetadata = $privateData[$myCommand.Name] - if ($authorMetadata) - {{ - # Copy the author-supplied hashtable so we can safely - # modify it. - $authorMetadata = @{{}} + $authorMetadata - if ($psBoundParameters['PSPrivateMetadata']) - {{ - # merge in the user-supplied metadata - foreach ($pair in $psPrivateMetadata.GetEnumerator()) - {{ - $authorMetadata[$pair.Key] = $pair.Value - }} - }} - # and update the bound parameter to include the merged data - $psBoundParameters['" + Constants.PrivateMetadata + @"'] = $authorMetadata - }} - }} - }} - - # Add in the input collection if there wasn't one explicitly passed - # which can only happen through PSParameterCollection - if (! $PSBoundParameters['" + Constants.PSInputCollection + @"']) - {{ - $PSBoundParameters['" + Constants.PSInputCollection + @"'] = $PSInputCollection - }} - - # Populate Verbose / Debug / Error from preference variables - if (-not $PSBoundParameters.ContainsKey('" + Constants.Verbose + @"')) - {{ - if($verbosePreference -in ""Continue"",""Inquire"") - {{ - $PSBoundParameters['" + Constants.Verbose + @"'] = [System.Management.Automation.SwitchParameter]::Present - }} - }} - - if (-not $PSBoundParameters.ContainsKey('" + Constants.Debug + @"')) - {{ - if($debugPreference -in ""Continue"",""Inquire"") - {{ - $PSBoundParameters['" + Constants.Debug + @"'] = [System.Management.Automation.SwitchParameter]::Present - }} - }} - - if (-not $PSBoundParameters.ContainsKey('" + Constants.ErrorAction + @"')) - {{ - $PSBoundParameters['" + Constants.ErrorAction + @"'] = $errorActionPreference - }} - - if(Test-Path variable:\errorActionPreference) - {{ - $errorAction = $errorActionPreference - }} - else - {{ - $errorAction = ""Continue"" - }} - - if ($PSBoundParameters['" + Constants.ErrorAction + @"'] -eq ""SilentlyContinue"") - {{ - $errorAction = ""SilentlyContinue"" - }} - - if($PSBoundParameters['" + Constants.ErrorAction + @"'] -eq ""Ignore"") - {{ - $PSBoundParameters['" + Constants.ErrorAction + @"'] = ""SilentlyContinue"" - $errorAction = ""SilentlyContinue"" - }} - - if ($PSBoundParameters['" + Constants.ErrorAction + @"'] -eq ""Inquire"") - {{ - $PSBoundParameters['" + Constants.ErrorAction + @"'] = ""Continue"" - $errorAction = ""Continue"" - }} - - if (-not $PSBoundParameters.ContainsKey('" + Constants.WarningAction + @"')) - {{ - $PSBoundParameters['" + Constants.WarningAction + @"'] = $warningPreference - }} - - if(Test-Path variable:\warningPreference) - {{ - $warningAction = $warningPreference - }} - else - {{ - $warningAction = ""Continue"" - }} - - if ($PSBoundParameters['" + Constants.WarningAction + @"'] -in ""SilentlyContinue"",""Ignore"") - {{ - $warningAction = ""SilentlyContinue"" - }} - - if ($PSBoundParameters['" + Constants.WarningAction + @"'] -eq ""Inquire"") - {{ - $PSBoundParameters['" + Constants.WarningAction + @"'] = ""Continue"" - $warningAction = ""Continue"" - }} - - - if (-not $PSBoundParameters.ContainsKey('" + Constants.InformationAction + @"')) - {{ - $PSBoundParameters['" + Constants.InformationAction + @"'] = $informationPreference - }} - - if(Test-Path variable:\informationPreference) - {{ - $informationAction = $informationPreference - }} - else - {{ - $informationAction = ""Continue"" - }} - - if ($PSBoundParameters['" + Constants.InformationAction + @"'] -in ""SilentlyContinue"",""Ignore"") - {{ - $informationAction = ""SilentlyContinue"" - }} - - if ($PSBoundParameters['" + Constants.InformationAction + @"'] -eq ""Inquire"") - {{ - $PSBoundParameters['" + Constants.InformationAction + @"'] = ""Continue"" - $informationAction = ""Continue"" - }} - - - # Create the final parameter collection... - $finalParameterCollection = $null - if ($null -ne $PSParameterCollection) - {{ - $finalParameterCollection = $PSParameterCollection - }} - else - {{ - $finalParameterCollection = $PSBoundParameters - }} - - try - {{ - # Start the workflow and return the job object... - $debuggerActive = (@(Get-PSBreakpoint).Count -gt 0) - if (($debuggerActive -eq $false) -and - ($null -ne $host) -and - ($null -ne $host.Runspace) -and - ($null -ne $host.Runspace.Debugger)) - {{ - $debuggerActive = $host.Runspace.Debugger.IsActive - }} - $job = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::StartWorkflowApplication( - $PSCmdlet, - $jobName, - '{1}', - $AsJob, - $parameterCollectionProcessed, - $finalParameterCollection, - $debuggerActive, - $SourceLanguageMode) - }} - catch - {{ - # extract exception from the error record - $e = $_.Exception - # this is probably a method invocation exception so we want the inner exception - # if it exists - if ($e -is [System.Management.Automation.MethodException] -and $e.InnerException) - {{ - $e = $e.InnerException - }} - - $msg = [Microsoft.PowerShell.Commands.ImportWorkflowCommand]::UnableToStartWorkflowMessageMessage -f ` - $MyInvocation.MyCommand.Name, $e.Message - - $newException = New-Object System.Management.Automation.RuntimeException $msg, $e - - throw (New-Object System.Management.Automation.ErrorRecord $newException, StartWorkflow.InvalidArgument, InvalidArgument, $finalParameterCollection) - }} - - if (-not $AsJob -and $null -ne $job) - {{ - try - {{ - Receive-Job -Job $job -Wait -Verbose -Debug -ErrorAction $errorAction -WarningAction $warningAction -InformationAction $informationAction - - $PSCmdlet.InvokeCommand.HasErrors = $job.State -eq 'failed' - }} - finally - {{ - if($job.State -ne ""Suspended"" -and $job.State -ne ""Stopped"") - {{ - Remove-Job -Job $job -Force - }} - else - {{ - $job - }} - }} - }} - else - {{ - $job - }} - }} -"; - #endregion Moved from AstToWorkflowConverter - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/LocalRunspaceProvider.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/LocalRunspaceProvider.cs deleted file mode 100644 index 0f8a7410539..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/LocalRunspaceProvider.cs +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright (c) 2011 Microsoft Corporation. All rights reserved - */ -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Security; -using System.Management.Automation.Tracing; -using System.Threading; -using Microsoft.PowerShell.Activities; - -namespace Microsoft.PowerShell.Workflow -{ - internal class LocalRunspaceAsyncResult : ConnectionAsyncResult - { - internal LocalRunspaceAsyncResult(object state, AsyncCallback callback, Guid ownerId) - :base(state, callback, ownerId) - { - - } - - internal Runspace Runspace { get; set; } - } - - internal class LocalRunspaceProvider : RunspaceProvider, IDisposable - { - private readonly TimeBasedCache _runspaceCache; - private readonly int _maxRunspaces; - private readonly PSLanguageMode? _languageMode; - - private readonly ConcurrentQueue _requests = - new ConcurrentQueue(); - - private readonly ConcurrentQueue _callbacks = - new ConcurrentQueue(); - - private int _isServicing; - private int _isServicingCallbacks; - private const int Servicing = 1; - private const int NotServicing = 0; - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// -1 indicates unlimited - /// - private const int MaxRunspacesPossible = -1; - private const int DefaultMaxRunspaces = MaxRunspacesPossible; - - internal TimeBasedCache RunspaceCache - { - get { return _runspaceCache; } - } - - internal LocalRunspaceProvider(int timeoutSeconds, PSLanguageMode? languageMode) : this(timeoutSeconds, DefaultMaxRunspaces, languageMode) - { - - } - - internal LocalRunspaceProvider(int timeoutSeconds, int maxRunspaces, PSLanguageMode? languageMode) - { - _runspaceCache = new TimeBasedCache(timeoutSeconds); - _maxRunspaces = maxRunspaces; - _languageMode = languageMode; - } - - /// - /// - /// - /// - /// - /// - /// - public override Runspace GetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval) - { - IAsyncResult asyncResult = BeginGetRunspace(connectionInfo, 0, 0, null, null); - - return EndGetRunspace(asyncResult); - } - - private Runspace AssignRunspaceIfPossible(PSLanguageMode? sourceLanguageMode = null) - { - Runspace runspace = null; - PSLanguageMode languageMode = (sourceLanguageMode != null) ? sourceLanguageMode.Value : - (_languageMode != null) ? _languageMode.Value : GetSystemLanguageMode(); - lock (_runspaceCache.TimerServicingSyncObject) - { - // Retrieve or create a local runspace having the same language mode as the source, if provided. - foreach (Item item in _runspaceCache.Cast>().Where(item => !item.Busy)) - { - if (item.Value.SessionStateProxy.LanguageMode == languageMode) - { - item.Idle = false; - item.Busy = true; - runspace = item.Value; - break; - } - } - - if ((runspace == null || runspace.RunspaceStateInfo.State != RunspaceState.Opened) && - (_maxRunspaces == MaxRunspacesPossible || _runspaceCache.Cache.Count < _maxRunspaces)) - { - runspace = CreateLocalActivityRunspace(languageMode); - - runspace.Open(); - _tracer.WriteMessage("New local runspace created"); - - _runspaceCache.Add(new Item(runspace, runspace.InstanceId)); - } - } - - return runspace; - } - - private static PSLanguageMode GetSystemLanguageMode() - { - return (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) ? - PSLanguageMode.ConstrainedLanguage : PSLanguageMode.FullLanguage; - } - - private void TraceThreadPoolInfo(string message) - { -#if DEBUG - int maxWorkerThreads, maxCompletionPortThreads, availableWorkerThreads, availableCompletionPortThreads, - minWorkerThreads, minCompletionPortThreads; - - ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads); - ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads); - ThreadPool.GetAvailableThreads(out availableWorkerThreads, out availableCompletionPortThreads); - - _tracer.WriteMessage(string.Format(System.Globalization.CultureInfo.InvariantCulture, - "LocalRunspaceProvider: {0} minwt:{1} mincpt:{2} wt:{3} ct:{4} awt:{5} act:{6}", - message, minWorkerThreads, minCompletionPortThreads, maxWorkerThreads, - maxCompletionPortThreads, availableWorkerThreads, - availableCompletionPortThreads)); -#else - _tracer.WriteMessage(string.Format(System.Globalization.CultureInfo.InvariantCulture, - "PSW ConnMgr: {0}", - message)); -#endif - } - - /// - /// Checks to see if a thread is already servicing and if not starts one - /// - private void CheckAndStartRequestServicingThread() - { - if (Interlocked.CompareExchange(ref _isServicing, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("QueueUserWorkItem Request Servicing thread"); - ThreadPool.QueueUserWorkItem(ServiceRequests); - } - - private void ServiceRequests(object state) - { - Debug.Assert(_maxRunspaces != MaxRunspacesPossible, - "When infinite runspaces are specified, then we should never hit the servicing thread"); - - lock(_runspaceCache.TimerServicingSyncObject) - { - LocalRunspaceAsyncResult asyncResult; - - Runspace runspace = AssignRunspaceIfPossible(); - - bool assigned = false; - while (runspace != null && _requests.TryDequeue(out asyncResult)) - { - asyncResult.Runspace = runspace; - assigned = true; - AddToPendingCallbacks(asyncResult); - - runspace = _runspaceCache.Cache.Count < _maxRunspaces ? AssignRunspaceIfPossible() : null; - } - - if (!assigned && runspace != null) - ReleaseRunspace(runspace); - } - - Interlocked.CompareExchange(ref _isServicing, NotServicing, Servicing); - } - - private void AddToPendingCallbacks(LocalRunspaceAsyncResult asyncResult) - { - _callbacks.Enqueue(asyncResult); - - if (Interlocked.CompareExchange(ref _isServicingCallbacks, Servicing, NotServicing) != NotServicing) return; - - TraceThreadPoolInfo("Callback thread"); - ThreadPool.QueueUserWorkItem(ServiceCallbacks); - } - - private void ServiceCallbacks(object state) - { - LocalRunspaceAsyncResult result; - while (_callbacks.TryDequeue(out result)) - { - result.SetAsCompleted(null); - } - Interlocked.CompareExchange(ref _isServicingCallbacks, NotServicing, Servicing); - } - - /// - /// Begin for obtaining a runspace for the specified ConnectionInfo - /// - /// connection info to be used for remote connections - /// number of times to retry - /// optional user defined callback - /// optional user specified state - /// time in milliseconds before the next retry has to be attempted - /// async result - public override IAsyncResult BeginGetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval, AsyncCallback callback, object state) - { - if (connectionInfo != null) - { - throw new InvalidOperationException(); - } - - LocalRunspaceAsyncResult asyncResult = new LocalRunspaceAsyncResult(state, callback, Guid.Empty); - - // Get the source language mode from the activity arguments if available and pass to runspace fetching. - PSLanguageMode? sourceLanguageMode = null; - RunCommandsArguments args = state as RunCommandsArguments; - if (args != null) - { - PSWorkflowRuntime wfRuntime = args.WorkflowHost as PSWorkflowRuntime; - if (wfRuntime != null) - { - PSWorkflowJob wfJob = wfRuntime.JobManager.GetJob(args.PSActivityContext.JobInstanceId); - if (wfJob != null) - { - sourceLanguageMode = wfJob.SourceLanguageMode; - } - } - } - - Runspace runspace = AssignRunspaceIfPossible(sourceLanguageMode); - if (runspace != null) - { - asyncResult.Runspace = runspace; - asyncResult.CompletedSynchronously = true; - asyncResult.SetAsCompleted(null); - } - else - { - // queue the request - _requests.Enqueue(asyncResult); - CheckAndStartRequestServicingThread(); - } - - return asyncResult; - } - - /// - /// End for obtaining a runspace for the specified connection info - /// - /// async result to end on - /// remote runspace to invoke commands on - public override Runspace EndGetRunspace(IAsyncResult asyncResult) - { - if (asyncResult == null) - throw new ArgumentNullException("asyncResult"); - - LocalRunspaceAsyncResult result = asyncResult as LocalRunspaceAsyncResult; - - if (result == null) - throw new ArgumentException(Resources.InvalidAsyncResultSpecified, "asyncResult"); - - // this will throw an exception when a runspace is not successfully - // available - result.EndInvoke(); - - Debug.Assert(result.Runspace != null, "EndInvoke() should throw an exception if runspace is null"); - - _tracer.WriteMessage("LocalRunspaceProvider: Request serviced and runspace returned"); - Runspace runspace = result.Runspace; - - Debug.Assert(runspace.RunspaceStateInfo.State == RunspaceState.Opened, "Only opened runspace should be returned"); - - return runspace; - } - - /// - /// - /// - /// - public override void ReleaseRunspace(Runspace runspace) - { - runspace.ResetRunspaceState(); - lock(_runspaceCache.TimerServicingSyncObject) - { - bool found = false; - foreach (Item item in - _runspaceCache.Cast>().Where(item => item.InstanceId == runspace.InstanceId)) - { - item.Busy = false; - found = true; - } - - if (!found) - throw new InvalidOperationException(); - } - - if (_maxRunspaces != MaxRunspacesPossible) - CheckAndStartRequestServicingThread(); - } - - private readonly static object SyncObject = new object(); - private static TypeTable _sharedTypeTable; - internal static TypeTable SharedTypeTable - { - get - { - if (_sharedTypeTable == null) - { - lock (SyncObject) - { - if (_sharedTypeTable == null) - { - _sharedTypeTable = TypeTable.LoadDefaultTypeFiles(); - } - } - } - return _sharedTypeTable; - } - } - - private static InitialSessionState GetInitialSessionStateWithSharedTypesAndNoFormat() - { - InitialSessionState initialSessionState = InitialSessionState.CreateDefault(); - - // clear existing types and format - initialSessionState.Types.Clear(); - initialSessionState.Formats.Clear(); - - // add default types and formats - initialSessionState.Types.Add(new SessionStateTypeEntry(SharedTypeTable.Clone(unshared: true))); - initialSessionState.DisableFormatUpdates = true; - - return initialSessionState; - } - - /// - /// Creates a local runspace with the autoloading turned on. - /// - /// - internal static Runspace CreateLocalActivityRunspace(PSLanguageMode? languageMode = null, bool useCurrentThreadForExecution = true) - { - InitialSessionState iss = GetInitialSessionStateWithSharedTypesAndNoFormat(); - if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) - { - iss.LanguageMode = PSLanguageMode.ConstrainedLanguage; - } - - if (languageMode != null && languageMode.HasValue) - { - iss.LanguageMode = languageMode.Value; - } - - // Add a variable indicating that we're in Workflow endpoint. This enables the - // autoloading feature. - SessionStateVariableEntry ssve = new SessionStateVariableEntry("RunningInPSWorkflowEndpoint", - true, "True if we're in a Workflow Endpoint", ScopedItemOptions.Constant); - iss.Variables.Add(ssve); - Runspace runspace = RunspaceFactory.CreateRunspace(iss); - - if (useCurrentThreadForExecution) - runspace.ThreadOptions = PSThreadOptions.UseCurrentThread; - - return runspace; - } - - /// - /// Dispose resources - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// - /// - /// - protected void Dispose(bool disposing) - { - if (disposing) - { - _runspaceCache.Dispose(); - } - } - - /// - /// Helper method only to use from test - /// - internal void Reset() - { - foreach(Item item in _runspaceCache) - { - item.Value.Dispose(); - } - _runspaceCache.Cache.Clear(); - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/NewPSWorkflowExecutionOptionCommand.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/NewPSWorkflowExecutionOptionCommand.cs deleted file mode 100644 index fcede3c6723..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/NewPSWorkflowExecutionOptionCommand.cs +++ /dev/null @@ -1,876 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Globalization; -using System.Management.Automation; -using System.Collections; -using System.Xml; -using Microsoft.PowerShell.Workflow; -using System.Reflection; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// This class is used to specify Workflow related options in the Register-PSSessionConfiguration - /// - public sealed class PSWorkflowExecutionOption : PSSessionTypeOption - { - private const string PrivateDataFormat = @"{0}"; - private const string ParamToken = @""; - - private string persistencePath = PSWorkflowConfigurationProvider.DefaultPersistencePath; - private long maxPersistenceStoreSizeGB = PSWorkflowConfigurationProvider.DefaultMaxPersistenceStoreSizeGB; - private bool persistWithEncryption = PSWorkflowConfigurationProvider.DefaultPersistWithEncryption; - private int maxRunningWorkflows = PSWorkflowConfigurationProvider.DefaultMaxRunningWorkflows; - private string[] allowedActivity = new List(PSWorkflowConfigurationProvider.DefaultAllowedActivity).ToArray(); - private bool enableValidation = PSWorkflowConfigurationProvider.DefaultEnableValidation; - private string[] outOfProcessActivity = new List(PSWorkflowConfigurationProvider.DefaultOutOfProcessActivity).ToArray(); - private int maxDisconnectedSessions = PSWorkflowConfigurationProvider.DefaultMaxDisconnectedSessions; - private int maxConnectedSessions = PSWorkflowConfigurationProvider.DefaultMaxConnectedSessions; - private int maxSessionsPerWorkflow = PSWorkflowConfigurationProvider.DefaultMaxSessionsPerWorkflow; - private int maxSessionsPerRemoteNode = PSWorkflowConfigurationProvider.DefaultMaxSessionsPerRemoteNode; - private int maxActivityProcesses = PSWorkflowConfigurationProvider.DefaultMaxActivityProcesses; - private int activityProcessIdleTimeoutSec = PSWorkflowConfigurationProvider.DefaultActivityProcessIdleTimeoutSec; - private int workflowApplicationPersistUnloadTimeoutSec = PSWorkflowConfigurationProvider.DefaultWorkflowApplicationPersistUnloadTimeoutSec; - private int wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = PSWorkflowConfigurationProvider.DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - private int activitiesCacheCleanupIntervalMSec = PSWorkflowConfigurationProvider.DefaultActivitiesCacheCleanupIntervalMSec; - private int remoteNodeSessionIdleTimeoutSec = PSWorkflowConfigurationProvider.DefaultRemoteNodeSessionIdleTimeoutSec; - private int sessionThrottleLimit = PSWorkflowConfigurationProvider.DefaultSessionThrottleLimit; - private int workflowShutdownTimeoutMSec = PSWorkflowConfigurationProvider.DefaultWorkflowShutdownTimeoutMSec; - - /// - /// Constructor that loads from default values - /// - internal PSWorkflowExecutionOption() - { - } - - private void ValidateRange(int min, int max, int value) - { - if (value >= min && value <= max) - return; - - string msg = string.Format(CultureInfo.InvariantCulture, Resources.ProvidedValueIsOutOfRange, value, min, max); - throw new ArgumentException(msg); - } - - /// - /// SessionThrottleLimit - /// - public int SessionThrottleLimit - { - get - { - return sessionThrottleLimit; - } - set { - ValidateRange(PSWorkflowConfigurationProvider.MinSessionThrottleLimit, PSWorkflowConfigurationProvider.MaxSessionThrottleLimit, value); - sessionThrottleLimit = value; - } - } - - /// - /// PersistencePath - /// - public string PersistencePath - { - get - { - return persistencePath; - } - set - { - persistencePath = value; - } - } - - /// - /// MaxPersistenceStoreSizeGB - /// - public long MaxPersistenceStoreSizeGB - { - get - { - return maxPersistenceStoreSizeGB; - } - set - { - maxPersistenceStoreSizeGB = value; - } - } - - /// - /// PersistWithEncryption - /// - public bool PersistWithEncryption - { - get { - return persistWithEncryption; - } - set - { - persistWithEncryption = value; - } - } - - /// - /// MaxRunningWorkflows - /// - public int MaxRunningWorkflows { - get - { - return maxRunningWorkflows; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxRunningWorkflows, PSWorkflowConfigurationProvider.MaxMaxRunningWorkflows, value); - maxRunningWorkflows = value; - } - } - - /// - /// AllowedActivity - /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] AllowedActivity - { - get - { - return allowedActivity; - } - set - { - allowedActivity = value; - } - } - - /// - /// OutOfProcActivity - /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] OutOfProcessActivity - { - get - { - return outOfProcessActivity; - } - set - { - outOfProcessActivity = value; - } - } - - - /// - /// EnableValidation - /// - public bool EnableValidation - { - get - { - return enableValidation; - } - set - { - enableValidation = value; - } - } - - /// - /// MaxDisconnectedSessions - /// - public int MaxDisconnectedSessions - { - get - { - return maxDisconnectedSessions; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxDisconnectedSessions, PSWorkflowConfigurationProvider.MaxMaxDisconnectedSessions, value); - maxDisconnectedSessions = value; - } - } - - /// - /// MaxConnectedSessions - /// - public int MaxConnectedSessions - { - get - { - return maxConnectedSessions; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxConnectedSessions, PSWorkflowConfigurationProvider.MaxMaxConnectedSessions, value); - maxConnectedSessions = value; - } - } - - /// - /// MaxSessionsPerWorkflow - /// - public int MaxSessionsPerWorkflow - { - get - { - return maxSessionsPerWorkflow; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxSessionsPerWorkflow, PSWorkflowConfigurationProvider.MaxMaxSessionsPerWorkflow, value); - maxSessionsPerWorkflow = value; - } - } - - /// - /// MaxSessionsPerRemoteNode - /// - public int MaxSessionsPerRemoteNode - { - get - { - return maxSessionsPerRemoteNode; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxSessionsPerRemoteNode, PSWorkflowConfigurationProvider.MaxMaxSessionsPerRemoteNode, value); - maxSessionsPerRemoteNode = value; - } - } - - /// - /// MaxActivityProcesses - /// - public int MaxActivityProcesses - { - get - { - return maxActivityProcesses; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinMaxActivityProcesses, PSWorkflowConfigurationProvider.MaxMaxActivityProcesses, value); - maxActivityProcesses = value; - } - } - - /// - /// WorkflowApplicationPersistUnloadTimeoutSec - /// - internal int WorkflowApplicationPersistUnloadTimeoutSec - { - get - { - return workflowApplicationPersistUnloadTimeoutSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinWorkflowApplicationPersistUnloadTimeoutSec, PSWorkflowConfigurationProvider.MaxWorkflowApplicationPersistUnloadTimeoutSec, value); - workflowApplicationPersistUnloadTimeoutSec = value; - } - } - - /// - /// WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec - /// - internal int WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec - { - get - { - return wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec, PSWorkflowConfigurationProvider.MaxWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec, value); - wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = value; - } - } - - /// - /// ActivitiesCacheCleanupIntervalMSec - /// - internal int ActivitiesCacheCleanupIntervalMSec - { - get - { - return activitiesCacheCleanupIntervalMSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinActivitiesCacheCleanupIntervalMSec, PSWorkflowConfigurationProvider.MaxActivitiesCacheCleanupIntervalMSec, value); - activitiesCacheCleanupIntervalMSec = value; - } - } - - /// - /// ActivityProcessIdleTimeOutSec - /// - public int ActivityProcessIdleTimeoutSec - { - get - { - return activityProcessIdleTimeoutSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinActivityProcessIdleTimeoutSec, PSWorkflowConfigurationProvider.MaxActivityProcessIdleTimeoutSec, value); - activityProcessIdleTimeoutSec = value; - } - } - - /// - /// RemoteNodeSessionIdleTimeOut - /// - public int RemoteNodeSessionIdleTimeoutSec - { - get - { - return remoteNodeSessionIdleTimeoutSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinRemoteNodeSessionIdleTimeoutSec, PSWorkflowConfigurationProvider.MaxRemoteNodeSessionIdleTimeoutSec, value); - remoteNodeSessionIdleTimeoutSec = value; - } - } - - /// - /// WorkflowShutdownTimeoutMSec - the maximum time allowed to suspend the workflows before aborting them. - /// - public int WorkflowShutdownTimeoutMSec - { - get - { - return workflowShutdownTimeoutMSec; - } - set - { - ValidateRange(PSWorkflowConfigurationProvider.MinWorkflowShutdownTimeoutMSec, PSWorkflowConfigurationProvider.MaxWorkflowShutdownTimeoutMSec, value); - workflowShutdownTimeoutMSec = value; - } - } - - /// - /// Copies values from updated. Only non default values are copies. - /// - /// - protected override void CopyUpdatedValuesFrom(PSSessionTypeOption updated) - { - if (updated == null) - throw new ArgumentNullException("updated"); - - PSWorkflowExecutionOption modified = updated as PSWorkflowExecutionOption; - if (modified == null) - throw new ArgumentNullException("updated"); - - if (modified.activityProcessIdleTimeoutSec != PSWorkflowConfigurationProvider.DefaultActivityProcessIdleTimeoutSec) - this.activityProcessIdleTimeoutSec = modified.activityProcessIdleTimeoutSec; - - if (modified.workflowApplicationPersistUnloadTimeoutSec != PSWorkflowConfigurationProvider.DefaultWorkflowApplicationPersistUnloadTimeoutSec) - this.workflowApplicationPersistUnloadTimeoutSec = modified.workflowApplicationPersistUnloadTimeoutSec; - - if (modified.wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec != PSWorkflowConfigurationProvider.DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec) - this.wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec = modified.wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec; - - if (modified.activitiesCacheCleanupIntervalMSec != PSWorkflowConfigurationProvider.DefaultActivitiesCacheCleanupIntervalMSec) - this.activitiesCacheCleanupIntervalMSec = modified.activitiesCacheCleanupIntervalMSec; - - if (!ListsMatch(modified.allowedActivity, PSWorkflowConfigurationProvider.DefaultAllowedActivity)) - this.allowedActivity = modified.allowedActivity; - - if (!string.Equals(modified.persistencePath, PSWorkflowConfigurationProvider.DefaultPersistencePath, StringComparison.OrdinalIgnoreCase)) - this.persistencePath = modified.persistencePath; - - if (modified.maxPersistenceStoreSizeGB != PSWorkflowConfigurationProvider.DefaultMaxPersistenceStoreSizeGB) - this.maxPersistenceStoreSizeGB = modified.maxPersistenceStoreSizeGB; - - if (modified.persistWithEncryption != PSWorkflowConfigurationProvider.DefaultPersistWithEncryption) - this.persistWithEncryption = modified.persistWithEncryption; - - if (modified.remoteNodeSessionIdleTimeoutSec != PSWorkflowConfigurationProvider.DefaultRemoteNodeSessionIdleTimeoutSec) - this.remoteNodeSessionIdleTimeoutSec = modified.remoteNodeSessionIdleTimeoutSec; - - if (modified.maxActivityProcesses != PSWorkflowConfigurationProvider.DefaultMaxActivityProcesses) - this.maxActivityProcesses = modified.maxActivityProcesses; - - if (modified.maxConnectedSessions != PSWorkflowConfigurationProvider.DefaultMaxConnectedSessions) - this.maxConnectedSessions = modified.maxConnectedSessions; - - if (modified.maxDisconnectedSessions != PSWorkflowConfigurationProvider.DefaultMaxDisconnectedSessions) - this.maxDisconnectedSessions = modified.maxDisconnectedSessions; - - if (modified.maxRunningWorkflows != PSWorkflowConfigurationProvider.DefaultMaxRunningWorkflows) - this.maxRunningWorkflows = modified.maxRunningWorkflows; - - if (modified.maxSessionsPerRemoteNode != PSWorkflowConfigurationProvider.DefaultMaxSessionsPerRemoteNode) - this.maxSessionsPerRemoteNode = modified.maxSessionsPerRemoteNode; - - if (modified.maxSessionsPerWorkflow != PSWorkflowConfigurationProvider.DefaultMaxSessionsPerWorkflow) - this.maxSessionsPerWorkflow = modified.maxSessionsPerWorkflow; - - if (!ListsMatch(modified.outOfProcessActivity, PSWorkflowConfigurationProvider.DefaultOutOfProcessActivity)) - this.outOfProcessActivity = modified.outOfProcessActivity; - - if (modified.enableValidation != PSWorkflowConfigurationProvider.DefaultEnableValidation) - this.enableValidation = modified.enableValidation; - - if (modified.sessionThrottleLimit != PSWorkflowConfigurationProvider.DefaultSessionThrottleLimit) - this.sessionThrottleLimit = modified.sessionThrottleLimit; - - if (modified.workflowShutdownTimeoutMSec != PSWorkflowConfigurationProvider.DefaultWorkflowShutdownTimeoutMSec) - this.workflowShutdownTimeoutMSec = modified.workflowShutdownTimeoutMSec; - } - - private static bool ListsMatch(IEnumerable a, IEnumerable b) - { - foreach (string strA in a) - { - bool found = false; - foreach (string strB in b) - { - if (string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase) == 0) - { - found = true; - break; - } - } - if (!found) - return false; - } - - foreach (string strB in b) - { - bool found = false; - foreach (string strA in a) - { - if (string.Compare(strB, strA, StringComparison.OrdinalIgnoreCase) == 0) - { - found = true; - break; - } - } - if (!found) - return false; - } - return true; - } - - /// - /// Returns a new instance constructed from privateData string. - /// - /// - protected override PSSessionTypeOption ConstructObjectFromPrivateData(string privateData) - { - return PSWorkflowConfigurationProvider.LoadConfig(privateData, null); - } - - /// - /// Implementation of the abstract method - /// - /// - protected override string ConstructPrivateData() - { - StringBuilder privateDataParams = new StringBuilder(); - - bool usesDefaultPath = string.Compare(persistencePath, PSWorkflowConfigurationProvider.DefaultPersistencePath, StringComparison.OrdinalIgnoreCase) != 0; - if (usesDefaultPath) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenPersistencePath, persistencePath)); - - if (maxPersistenceStoreSizeGB != PSWorkflowConfigurationProvider.DefaultMaxPersistenceStoreSizeGB) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxPersistenceStoreSizeGB, maxPersistenceStoreSizeGB)); - - if (persistWithEncryption != PSWorkflowConfigurationProvider.DefaultPersistWithEncryption) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenPersistWithEncryption, persistWithEncryption)); - - if (maxRunningWorkflows != PSWorkflowConfigurationProvider.DefaultMaxRunningWorkflows) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxRunningWorkflows, maxRunningWorkflows)); - - if (enableValidation != PSWorkflowConfigurationProvider.DefaultEnableValidation) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenEnableValidation, enableValidation)); - - StringBuilder allowedAttribute = new StringBuilder(); - foreach (string a in allowedActivity ?? new string[0]) - { - allowedAttribute.Append(a); - allowedAttribute.Append(','); - } - if (allowedAttribute.Length > 0) - { - allowedAttribute.Remove(allowedAttribute.Length - 1, 1); - } - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenAllowedActivity, allowedAttribute.ToString())); - - StringBuilder outOfProcAttribute = new StringBuilder(); - foreach (string a in outOfProcessActivity ?? new string[0]) - { - outOfProcAttribute.Append(a); - outOfProcAttribute.Append(','); - } - if (outOfProcAttribute.Length > 0) - { - outOfProcAttribute.Remove(outOfProcAttribute.Length - 1, 1); - } - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenOutOfProcessActivity, outOfProcAttribute.ToString())); - - if (maxDisconnectedSessions != PSWorkflowConfigurationProvider.DefaultMaxDisconnectedSessions) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxDisconnectedSessions, maxDisconnectedSessions)); - - if (maxConnectedSessions != PSWorkflowConfigurationProvider.DefaultMaxConnectedSessions) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxConnectedSessions, maxConnectedSessions)); - - if (maxSessionsPerWorkflow != PSWorkflowConfigurationProvider.DefaultMaxSessionsPerWorkflow) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxSessionsPerWorkflow, maxSessionsPerWorkflow)); - - if (maxSessionsPerRemoteNode != PSWorkflowConfigurationProvider.DefaultMaxSessionsPerRemoteNode) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxSessionsPerRemoteNode, maxSessionsPerRemoteNode)); - - if (maxActivityProcesses != PSWorkflowConfigurationProvider.DefaultMaxActivityProcesses) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenMaxActivityProcesses, maxActivityProcesses)); - - if (activityProcessIdleTimeoutSec != PSWorkflowConfigurationProvider.DefaultActivityProcessIdleTimeoutSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenActivityProcessIdleTimeoutSec, activityProcessIdleTimeoutSec)); - - if (workflowApplicationPersistUnloadTimeoutSec != PSWorkflowConfigurationProvider.DefaultWorkflowApplicationPersistUnloadTimeoutSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenWorkflowApplicationPersistUnloadTimeoutSec, workflowApplicationPersistUnloadTimeoutSec)); - - if (wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec != PSWorkflowConfigurationProvider.DefaultWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenWSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec, wsmanPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec)); - - if (activitiesCacheCleanupIntervalMSec != PSWorkflowConfigurationProvider.DefaultActivitiesCacheCleanupIntervalMSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenActivitiesCacheCleanupIntervalMSec, activitiesCacheCleanupIntervalMSec)); - - if (remoteNodeSessionIdleTimeoutSec != PSWorkflowConfigurationProvider.DefaultRemoteNodeSessionIdleTimeoutSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenRemoteNodeSessionIdleTimeoutSec, remoteNodeSessionIdleTimeoutSec)); - - if (sessionThrottleLimit != PSWorkflowConfigurationProvider.DefaultSessionThrottleLimit) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenSessionThrottleLimit, sessionThrottleLimit)); - - if (workflowShutdownTimeoutMSec != PSWorkflowConfigurationProvider.DefaultWorkflowShutdownTimeoutMSec) - privateDataParams.Append(string.Format(CultureInfo.InvariantCulture, ParamToken, PSWorkflowConfigurationProvider.TokenWorkflowShutdownTimeoutMSec, workflowShutdownTimeoutMSec)); - - return string.Format(CultureInfo.InvariantCulture, PrivateDataFormat, privateDataParams.ToString()); - } - - internal string ConstructPrivateDataInternal() - { - return ConstructPrivateData(); - } - } - - /// - /// Command to create an object for PSWorkflowExecutionOption - /// - [Cmdlet(VerbsCommon.New, "PSWorkflowExecutionOption",HelpUri = "https://go.microsoft.com/fwlink/?LinkID=210609")] - [OutputType(typeof(PSWorkflowExecutionOption))] - public sealed class NewPSWorkflowExecutionOptionCommand : PSCmdlet - { - private PSWorkflowExecutionOption option = new PSWorkflowExecutionOption(); - private bool enableValidationParamSpecified = false; - - /// - /// PersistencePath - /// - [Parameter] - public string PersistencePath - { - get - { - return option.PersistencePath; - } - set - { - if (value != null) - { - string rootedPath = value; - bool isPathTooLong = false; - if (!Path.IsPathRooted(value)) - { - try - { - rootedPath = Path.GetPathRoot(value); - - if (String.IsNullOrEmpty(rootedPath)) - { - rootedPath = value; - } - } - catch (PathTooLongException) - { - isPathTooLong = true; - } - } - - if (isPathTooLong || (rootedPath != null && rootedPath.Length > Constants.MaxAllowedPersistencePathLength)) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, Resources.PersistencePathToolLong, value, Constants.MaxAllowedPersistencePathLength)); - } - } - - option.PersistencePath = value; - } - } - - /// - /// MaxPersistenceStoreSizeGB - /// - [Parameter] - public long MaxPersistenceStoreSizeGB - { - get - { - return option.MaxPersistenceStoreSizeGB; - } - set - { - option.MaxPersistenceStoreSizeGB = value; - } - } - - /// - /// UseEncryption - /// - [Parameter] - public SwitchParameter PersistWithEncryption { - get - { - return option.PersistWithEncryption; - } - set - { - option.PersistWithEncryption = value; - } - } - - /// - /// MaxRunningWorkflows - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxRunningWorkflows, PSWorkflowConfigurationProvider.MaxMaxRunningWorkflows)] - public int MaxRunningWorkflows { - get - { - return option.MaxRunningWorkflows; - } - set - { - option.MaxRunningWorkflows = value; - } - } - - /// - /// AllowedActivity - /// - [Parameter, SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] AllowedActivity - { - get - { - return option.AllowedActivity; - } - set - { - option.AllowedActivity = value; - } - } - - /// - /// OutOfProcActivity - /// - [Parameter, SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] OutOfProcessActivity - { - get - { - return option.OutOfProcessActivity; - } - set - { - option.OutOfProcessActivity = value; - } - } - - /// - /// EnableValidation - /// - [Parameter] - public SwitchParameter EnableValidation - { - get - { - return option.EnableValidation; - } - set - { - option.EnableValidation = value; - enableValidationParamSpecified = true; - } - } - - /// - /// MaxDisconnectedSessions - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxDisconnectedSessions, PSWorkflowConfigurationProvider.MaxMaxDisconnectedSessions)] - public int MaxDisconnectedSessions - { - get - { - return option.MaxDisconnectedSessions; - } - set - { - option.MaxDisconnectedSessions = value; - } - } - - /// - /// MaxConnectedSessions - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxConnectedSessions, PSWorkflowConfigurationProvider.MaxMaxConnectedSessions)] - public int MaxConnectedSessions - { - get - { - return option.MaxConnectedSessions; - } - set - { - option.MaxConnectedSessions = value; - } - } - - /// - /// MaxSessionsPerWorkflow - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxSessionsPerWorkflow, PSWorkflowConfigurationProvider.MaxMaxSessionsPerWorkflow)] - public int MaxSessionsPerWorkflow - { - get - { - return option.MaxSessionsPerWorkflow; - } - set - { - option.MaxSessionsPerWorkflow = value; - } - } - - /// - /// MaxSessionsPerRemoteNode - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxSessionsPerRemoteNode, PSWorkflowConfigurationProvider.MaxMaxSessionsPerRemoteNode)] - public int MaxSessionsPerRemoteNode - { - get - { - return option.MaxSessionsPerRemoteNode; - } - set - { - option.MaxSessionsPerRemoteNode = value; - } - } - - /// - /// MaxActivityProcess - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinMaxActivityProcesses, PSWorkflowConfigurationProvider.MaxMaxActivityProcesses)] - public int MaxActivityProcesses - { - get - { - return option.MaxActivityProcesses; - } - set - { - option.MaxActivityProcesses = value; - } - } - - /// - /// ActivityProcessIdleTimeOutSec - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinActivityProcessIdleTimeoutSec, PSWorkflowConfigurationProvider.MaxActivityProcessIdleTimeoutSec)] - public int ActivityProcessIdleTimeoutSec - { - get - { - return option.ActivityProcessIdleTimeoutSec; - } - set - { - option.ActivityProcessIdleTimeoutSec = value; - } - } - - /// - /// RemoteNodeSessionIdleTimeOutSec - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinRemoteNodeSessionIdleTimeoutSec, PSWorkflowConfigurationProvider.MaxRemoteNodeSessionIdleTimeoutSec)] - public int RemoteNodeSessionIdleTimeoutSec - { - get - { - return option.RemoteNodeSessionIdleTimeoutSec; - } - set - { - option.RemoteNodeSessionIdleTimeoutSec = value; - } - } - - /// - /// SessionThrottleLimit - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinSessionThrottleLimit, PSWorkflowConfigurationProvider.MaxSessionThrottleLimit)] - public int SessionThrottleLimit - { - get - { - return option.SessionThrottleLimit; - } - set - { - option.SessionThrottleLimit = value; - } - } - - /// - /// WorkflowShutdownTimeoutMSec - the maximum time allowed to suspend the workflows before aborting them. - /// - [Parameter, ValidateRange(PSWorkflowConfigurationProvider.MinWorkflowShutdownTimeoutMSec, PSWorkflowConfigurationProvider.MaxWorkflowShutdownTimeoutMSec)] - public int WorkflowShutdownTimeoutMSec - { - get - { - return option.WorkflowShutdownTimeoutMSec; - } - set - { - option.WorkflowShutdownTimeoutMSec = value; - } - } - - /// - /// ProcessRecord - /// - protected override void ProcessRecord() - { - // By default EnableValidation should be TRUE for NewPSWorkflowExecutionOption, so that activity validation is done in user created endpoints. - // - if(!enableValidationParamSpecified) - { - option.EnableValidation = true; - } - - this.WriteObject(option); - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityBase.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityBase.cs deleted file mode 100644 index 265ec303569..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityBase.cs +++ /dev/null @@ -1,6577 +0,0 @@ -using System; -using System.Collections; -using System.Data; -using System.Linq; -using System.Activities; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; -using System.Management.Automation.Remoting; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Activities.Hosting; -using System.Activities.Statements; -using System.ComponentModel; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Management.Automation.Runspaces; -using Microsoft.Management.Infrastructure; -using Microsoft.Management.Infrastructure.Options; -using System.Management.Automation.Tracing; -using Dbg=System.Diagnostics.Debug; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation.Host; -using System.Management; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - internal class ActivityParameters - { - internal uint? ConnectionRetryCount - { - get; - private set; - } - - internal uint? ConnectionRetryInterval - { - get; - private set; - } - - internal uint? ActionRetryCount - { - get; - private set; - } - - internal uint? ActionRetryInterval - { - get; - private set; - } - - internal string[] PSRequiredModules - { - get; - private set; - } - - internal ActivityParameters(uint? connectionRetryCount, uint? connectionRetryInterval, uint? actionRetryCount, uint? actionRetryInterval, string[] requiredModule) - { - this.ConnectionRetryCount = connectionRetryCount; - this.ConnectionRetryInterval = connectionRetryInterval; - this.ActionRetryCount = actionRetryCount; - this.ActionRetryInterval = actionRetryInterval; - this.PSRequiredModules = requiredModule; - } - } - - internal delegate void PrepareSessionDelegate(ActivityImplementationContext implementationContext); - - internal class RunCommandsArguments - { - internal ActivityParameters ActivityParameters { get; private set; } - internal PSDataCollection Output { get; private set; } - internal PSDataCollection Input { get; private set; } - internal PSActivityContext PSActivityContext { get; private set; } - internal Dictionary ParameterDefaults { get; private set; } - internal Type ActivityType { get; private set; } - internal PrepareSessionDelegate Delegate { get; private set; } - internal object ActivityObject { get; private set; } - internal PSWorkflowHost WorkflowHost { get; private set; } - internal ActivityImplementationContext ImplementationContext { get; private set; } - internal int CommandExecutionType { get; private set; } - internal System.Management.Automation.PowerShell HelperCommand { get; set; } - internal PSDataCollection HelperCommandInput { get; set; } - internal int CleanupTimeout { get; set; } - - internal RunCommandsArguments(ActivityParameters activityParameters, PSDataCollection output, PSDataCollection input, - PSActivityContext psActivityContext, PSWorkflowHost workflowHost, - bool runInProc, Dictionary parameterDefaults, Type activityType, - PrepareSessionDelegate prepareSession, object activityObject, ActivityImplementationContext implementationContext) - { - ActivityParameters = activityParameters; - Output = output; - Input = input; - PSActivityContext = psActivityContext; - ParameterDefaults = parameterDefaults; - ActivityType = activityType; - Delegate = prepareSession; - ActivityObject = activityObject; - WorkflowHost = workflowHost; - ImplementationContext = implementationContext; - - CommandExecutionType = - DetermineCommandExecutionType( - implementationContext.ConnectionInfo, runInProc, - activityType, psActivityContext); - } - - internal static int DetermineCommandExecutionType(WSManConnectionInfo connectionInfo, bool runInProc, Type activityType, PSActivityContext psActivityContext) - { - // check for cleanup activity first - if (typeof(PSCleanupActivity).IsAssignableFrom(activityType)) - { - return PSActivity.CleanupActivity; - } - - if (connectionInfo != null) - { - // check if this is an activity with custom remoting - // then we need to run it in proc - if (psActivityContext != null && psActivityContext.RunWithCustomRemoting) - return PSActivity.CommandRunInProc; - - return PSActivity.CommandRunRemotely; - } - - // if connectionInfo is null, then the command needs to be executed - // on this machine. #1 to #3 from above is possible - if (!runInProc) - { - return PSActivity.CommandRunOutOfProc; - } - - // at this point the command is being run in-proc - - // Check for WMI activity - if (typeof(WmiActivity).IsAssignableFrom(activityType) || typeof(GenericCimCmdletActivity).IsAssignableFrom(activityType)) - { - return PSActivity.RunInProcNoRunspace; - } - - if (typeof(PSGeneratedCIMActivity).IsAssignableFrom(activityType)) - { - return PSActivity.CimCommandRunInProc; - } - - return PSActivity.CommandRunInProc; - } - } - - /// - /// Implementing this interface indicates that the activity supports connection retry. - /// - public interface IImplementsConnectionRetry - { - /// - /// Defines the number of retries that the activity will make to connect to a remote - /// machine when it encounters an error. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - InArgument PSConnectionRetryCount - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between connection retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - InArgument PSConnectionRetryIntervalSec - { - get; - set; - } - - } - - /// - /// Special variables that can be defined in a workflow to - /// control the behaviour of PowerShell activities. - /// - public static class WorkflowPreferenceVariables - { - /// - /// The parent activity ID to be used for all progress records - /// that are written in the enclosing scope - /// - public const string PSParentActivityId = "PSParentActivityID"; - - /// - /// Workflow variable that controls when activities are run - /// in process. If true, all activities in the enclosing scope - /// will be run in process - /// - public const string PSRunInProcessPreference = "PSRunInProcessPreference"; - - /// - /// Workflow variable that is used to determine if a PowerShell activity should - /// persist when it's done. if true, then all PSActivities in that scope - /// will not persist at the end of their execution. - /// - public const string PSPersistPreference = "PSPersistPreference"; - } - - /// - /// Parameter default, contains all the information which needs to be passed to Workflow context. - /// - public sealed class HostParameterDefaults : IDisposable - { - /// - /// Default constructor. - /// - public HostParameterDefaults() - { - Parameters = new Dictionary(); - HostCommandMetadata = new HostSettingCommandMetadata(); - Runtime = null; - HostPersistenceDelegate = null; - ActivateDelegate = null; - AsyncExecutionCollection = null; - RemoteActivityState = null; - } - - /// - /// All the activity level default parameter values are passed here. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary Parameters - { - get; - set; - } - - /// - /// Metadata / symbolic information about the currently-running workflow. - /// - public HostSettingCommandMetadata HostCommandMetadata - { - get; - set; - } - - /// - /// Job instance id. - /// - public Guid JobInstanceId - { - get; - set; - } - - /// - /// The workflow runtime. - /// - public PSWorkflowHost Runtime - { - get; - set; - } - - /// - /// The host persistence delegate. - /// - public Func HostPersistenceDelegate - { - get; - set; - } - - /// - /// The Workflow activation delegate. - /// - public Action ActivateDelegate - { - get; - set; - } - - /// - /// The asynchronous execution collection. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary AsyncExecutionCollection - { - get; - set; - } - - /// - /// Currently executing remote activities state with runspace id or completion state. - /// - public PSWorkflowRemoteActivityState RemoteActivityState - { - get; - set; - } - - /// - /// Dispose method - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose of managed resources - /// - /// true if disposing - private void Dispose(bool disposing) - { - if (!disposing) return; - - Parameters = null; - ActivateDelegate = null; - AsyncExecutionCollection = null; - RemoteActivityState = null; - HostPersistenceDelegate = null; - Runtime = null; - } - } - - /// - /// Runtime metadata that represents the currently-running command. - /// - public class HostSettingCommandMetadata - { - /// - /// The command name that generated this workflow. - /// - public string CommandName - { - get; - set; - } - - /// - /// The start line of the command name that generated this section of - /// the workflow. - /// - public int StartLineNumber - { - get; - set; - } - - /// - /// The start column of the command name that generated this section of - /// the workflow. - /// - public int StartColumnNumber - { - get; - set; - } - - /// - /// The end line of the command name that generated this section of - /// the workflow. - /// - public int EndLineNumber - { - get; - set; - } - - /// - /// The end column of the command name that generated this section of - /// the workflow. - /// - public int EndColumnNumber - { - get; - set; - } - } - - /// - /// The activity context. - /// - [Serializable] - public class PSActivityContext : IDisposable - { - // Holds our list of commands to run, mapped to a RetryCount class of: - // Connection Attempts, Action Attempts - - [NonSerialized] - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - [NonSerialized] - internal Dictionary runningCommands; - - [NonSerialized] - internal ConcurrentQueue commandQueue; - - /// - /// IsCanceled. - /// - public bool IsCanceled { get; set; } - internal Dictionary UserVariables = new Dictionary(); - internal List exceptions = new List(); - internal PSDataCollection errors = new PSDataCollection(); - internal bool Failed; - internal bool SuspendOnError; - - [NonSerialized] - internal readonly ConcurrentQueue AsyncResults = new ConcurrentQueue(); - - [NonSerialized] - internal EventHandler HandleRunspaceStateChanged; - - [NonSerialized] - internal PSDataCollection progress; - - [NonSerialized] internal object SyncRoot = new object(); - [NonSerialized] internal bool AllCommandsStarted; - [NonSerialized] internal int CommandsRunningCount = 0; - - [NonSerialized] internal WaitCallback Callback; - [NonSerialized] internal object AsyncState; - [NonSerialized] internal Guid JobInstanceId; - - [NonSerialized] - internal ActivityParameters ActivityParams; - - [NonSerialized] - internal PSDataCollection Input; - - [NonSerialized] - internal PSDataCollection Output; - - [NonSerialized] - internal PSWorkflowHost WorkflowHost; - - [NonSerialized] - internal HostParameterDefaults HostExtension; - - [NonSerialized] - internal bool RunInProc; - - [NonSerialized] - internal bool MergeErrorToOutput; - - [NonSerialized] - internal Dictionary ParameterDefaults; - - [NonSerialized] - internal Type ActivityType; - - [NonSerialized] - internal PrepareSessionDelegate PrepareSession; - - [NonSerialized] - internal object ActivityObject; - - /// - /// The .NET type implementing the cmdlet to call, used for - /// direct-call cmdlets. - /// - internal Type TypeImplementingCmdlet { get; set; } - - internal bool RunWithCustomRemoting { get; set; } - - /// - /// Cancelling the Async execution. - /// - public void Cancel() - { - if (WorkflowHost == null) - { - throw new InvalidOperationException("WorkflowHost"); - } - - // First, clear pending items - if (this.commandQueue != null) - { - while (!this.commandQueue.IsEmpty) - { - ActivityImplementationContext implementationContext; - bool gotCommand = this.commandQueue.TryDequeue(out implementationContext); - if (gotCommand) - { - System.Management.Automation.PowerShell command = implementationContext.PowerShellInstance; - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity: Cancelling pending command {0}.", command)); - - command.Dispose(); - } - } - } - - // cancel all pending invocations in activityhostmanager - PSResumableActivityHostController resumablecontroller = WorkflowHost.PSActivityHostController as PSResumableActivityHostController; - if (resumablecontroller != null) - { - resumablecontroller.StopAllResumablePSCommands(this.JobInstanceId); - } - else - { - PSOutOfProcessActivityController delegateController = WorkflowHost.PSActivityHostController as PSOutOfProcessActivityController; - if (delegateController != null) - { - while (this.AsyncResults.Count > 0) - { - IAsyncResult result; - this.AsyncResults.TryDequeue(out result); - delegateController.CancelInvokePowerShell(result); - } - } - } - - while (this.runningCommands.Count > 0) - { - System.Management.Automation.PowerShell currentCommand = null; - - lock (this.runningCommands) - { - foreach (System.Management.Automation.PowerShell command in this.runningCommands.Keys) - { - currentCommand = command; - break; - } - - if (currentCommand == null) - { - return; - } - } - - if (currentCommand.InvocationStateInfo.State == PSInvocationState.Running) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity: Stopping command {0}.", currentCommand)); - - try - { - currentCommand.Stop(); - } - catch (NullReferenceException) - { - // The PowerShell API has a bug where it sometimes throws NullReferenceException - // when being stopped. Ignore in this case. - } - catch (InvalidOperationException) - { - // It's possible that the pipeline stopped just before stopping it. - // (In-process case) - } - } - - - // If anything fails, then set the overall failure state for the activity... - if (currentCommand.InvocationStateInfo.State != PSInvocationState.Completed || currentCommand.HadErrors) - { - this.Failed = true; - } - - int commandType = - RunCommandsArguments.DetermineCommandExecutionType( - currentCommand.Runspace.ConnectionInfo as WSManConnectionInfo, RunInProc, ActivityType, this); - if (commandType != PSActivity.RunInProcNoRunspace) - { - PSActivity.CloseRunspaceAndDisposeCommand(currentCommand, WorkflowHost, this, commandType); - } - - this.runningCommands.Remove(currentCommand); - } - } - - /// - /// - /// - /// - /// This function is designed to be lightweight and to - /// run on the Workflow thread when Execute() is called. When - /// any changes are made to this function the contract needs to - /// be honored - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Microsoft.Reliability", - "CA2000:Dispose objects before losing scope", - Justification = "Disposed in EndExecute.")] - public bool Execute() - { - // Create the template of a thread that just pulls from the commandQueue - // and processes the SMA.PowerShell given to it. - /* - - A note about cancellation - ------------------------- - - While a worker thread is running, there's - the possibility of the user cancelling the activity as a whole. - That means, at any line, there's the potential of us wanting - to stop what the thread is doing. - In the worst case of a race condition, the user attempts to cancel - the activity, but we ignore it. - The natural way to prevent this kind of race condition is through - locking so that the 'if(canceled)' condition doesn't change during - individual lines of the thread's invocation. - However, adding this lock would mean that the running thread would - _have_ the lock, which would would prevent the cancellation thread - from _entering_ the lock, preventing the cancellation thread from - signalling its condition. This is, in fact, the very worst case of - the race condition: the user wants to cancel the activity, but we - ignore it. - Verification of cancellation, then, is on a "best-effort" basis. - */ - - if (commandQueue == null) - { - throw new InvalidOperationException("commandQueue"); - } - - ActivityImplementationContext implementationContext; - bool result = commandQueue.TryDequeue(out implementationContext); - - lock (SyncRoot) - { - while (result) - { - RunCommandsArguments args = new RunCommandsArguments(ActivityParams, Output, Input, - this, WorkflowHost, RunInProc, - ParameterDefaults, ActivityType, PrepareSession, - ActivityObject, implementationContext); - // increment count of running commands - Interlocked.Increment(ref CommandsRunningCount); - PSActivity.BeginRunOneCommand(args); - - result = commandQueue.TryDequeue(out implementationContext); - } - - // at this point all commands in the activity context should have been - // started - AllCommandsStarted = true; - } - - return true; - } - - /// - /// Dispose - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose - /// - /// - [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", Justification = "errors is disposed at the time workflow is removed.")] - protected virtual void Dispose(bool disposing) - { - if (!disposing) - return; - - _tracer.Dispose(); - - Callback = null; - PrepareSession = null; - HandleRunspaceStateChanged = null; - ActivityObject = null; - ParameterDefaults = null; - Input = null; - Output = null; - errors = null; - progress = null; - WorkflowHost = null; - ActivityParams = null; - exceptions = null; - runningCommands = null; - commandQueue = null; - - } - } - - /// - /// Defines the activity on-resume action. - /// - public enum ActivityOnResumeAction - { - /// - /// Indicates the resumption is normal. - /// - Resume = 0, - - /// - /// Indicates that the activity needs to be restarted. - /// - Restart = 1, - } - - /// - /// Activities derived from this class can be used in the Pipeline activity - /// - public abstract class PipelineEnabledActivity : NativeActivity - { - - /// - /// The Input stream for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument> Input - { - get; - set; - } - - /// - /// Determines whether to connect the input stream for this activity. - /// - [InputAndOutputCategory] - [DefaultValue(false)] - public bool UseDefaultInput - { - get; - set; - } - - /// - /// The output stream from the activity - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> Result - { - get; - set; - } - - /// - /// Determines whether to append output to Result. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public bool? AppendOutput - { - get; - set; - } - } - - internal class BookmarkContext - { - internal PSWorkflowInstanceExtension BookmarkResumingExtension { get; set; } - internal Bookmark CurrentBookmark { get; set; } - } - - /// - /// Base class for PowerShell-based workflow activities - /// - public abstract class PSActivity : PipelineEnabledActivity - { - /// - /// The bookmark prefix for Powershell activities. - /// - public static readonly string PSBookmarkPrefix = "Microsoft_PowerShell_Workflow_Bookmark_"; - - /// - /// The bookmark prefix for Powershell suspend activities. - /// - public static readonly string PSSuspendBookmarkPrefix = "Microsoft_PowerShell_Workflow_Bookmark_Suspend_"; - - /// - /// The bookmark prefix for Powershell persist activities. - /// - public static readonly string PSPersistBookmarkPrefix = "Microsoft_PowerShell_Workflow_Bookmark_PSPersist_"; - - /// - /// Constructor for the PSActivity class. - /// - protected PSActivity() - { - cancelTimer = new Delay - { - Duration = new InArgument((context) => - new TimeSpan(0, 0, (int)context.GetValue(this.PSActionRunningTimeoutSec))) - }; - } - - /// - /// Gets the fully qualified name of the command invoked by this activity. - /// - public virtual string PSCommandName - { - get - { - return String.Empty; - } - } - - /// - /// Returns the module defining the command called by this activity. - /// It may be null. - /// - protected virtual string PSDefiningModule - { - get - { - return null; - } - } - - /// - /// Tracer initialization. - /// - protected PowerShellTraceSource Tracer - { - get - { - return _tracer; - } - } - - /// - /// In addition to the display name PSProgress Message will provide - /// the way to append the additional information into the activity progress message - /// like branch name or iteration number in case of parallel foreach. - /// - [DefaultValue(null)] - public InArgument PSProgressMessage - { - get; - set; - } - - /// - /// The Error stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSError - { - get; - set; - } - - /// - /// The Progress stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSProgress - { - get; - set; - } - - /// - /// The Verbose stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSVerbose - { - get; - set; - } - - /// - /// The Debug stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSDebug - { - get; - set; - } - - /// - /// The Warning stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSWarning - { - get; - set; - } - - /// - /// The Information stream / collection for the activity. - /// - [InputAndOutputCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InOutArgument> PSInformation - { - get; - set; - } - - /// - /// Forces the activity to return non-serialized objects. Resulting objects - /// have functional methods and properties (as opposed to serialized versions - /// of them), but will not survive persistence when the Workflow crashes or is - /// persisted. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSDisableSerialization - { - get; - set; - } - - /// - /// Forces the activity to not call the persist functionality, which will be responsible for - /// persisting the workflow state onto the disk. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSPersist - { - get; - set; - } - - /// - /// Determines whether to merge error data to the output stream - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument MergeErrorToOutput - { - get; - set; - } - - /// - /// Defines the maximum amount of time, in seconds, that this activity may run. - /// The default is unlimited. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSActionRunningTimeoutSec - { - get; - set; - } - - /// - /// This the list of module names (or paths) that are required to run this Activity successfully. - /// The default is null. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSRequiredModules - { - get; - set; - } - - /// - /// Defines the number of retries that the activity will make when it encounters - /// an error during execution of its action. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSActionRetryCount - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between action retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSActionRetryIntervalSec - { - get; - set; - } - - /// - /// Determines whether to emit verbose output of the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument Verbose - { - get; - set; - } - - /// - /// Determines whether to emit debug output of the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument Debug - { - get; - set; - } - - /// - /// Determines how errors should be handled by the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument ErrorAction - { - get; - set; - } - - /// - /// Determines how warnings should be handled by the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument WarningAction - { - get; - set; - } - - /// - /// Determines how information records should be handled by the activity. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument InformationAction - { - get; - set; - } - - /// - /// Provides access to the parameter defaults dictionary - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design","CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is forced by the interaction of PowerShell and Workflow.")] - protected Variable> ParameterDefaults - { - get; - set; - } - - private Variable psActivityContextImplementationVariable = new Variable("psActivityContextImplementationVariable"); - private Variable psRunningTimeoutDelayActivityInstanceVar = new Variable("psRunningTimeoutDelayActivityInstanceVar"); - private Delay cancelTimer; - - // Instance variables. The following are OK to be an instance variables, as they are - // not modified during runtime. If any variable holds activity-specific state, - // it must be stored in PSActivityContext. - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer _structuredTracer = new Tracer(); - - private Variable noPersistHandle = new Variable(); - private Variable bookmarking = new Variable(); - private TerminateWorkflow terminateActivity = new TerminateWorkflow() { Reason = Resources.RunningTimeExceeded }; - private SuspendOnError suspendActivity = new SuspendOnError(); - - /// - /// Records a non-terminating error. If the runtime has associated an error stream, this - /// error will be written to that stream. Otherwise, this will be thrown as an exception. - /// - /// The exception associated with the error. - /// The error ID associated with the error. This should be a non-localized string. - /// The error category associated with the error. - /// The object that was being processed while encountering this error. - /// The powershell activity context. - private static void WriteError(Exception exception, string errorId, ErrorCategory errorCategory, object originalTarget, PSActivityContext psActivityContext) - { - if (psActivityContext.errors != null) - { - ErrorRecord errorRecord = new ErrorRecord(exception, errorId, errorCategory, originalTarget); - lock (psActivityContext.errors) - { - psActivityContext.errors.Add(errorRecord); - } - } - else - { - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(exception); - } - } - } - - /// - /// In order for an activity to go idle, 'CanInduceIdle' should be true. - /// - protected override bool CanInduceIdle - { - get - { - return true; - } - } - - /// - /// Begins the execution of the activity. - /// - /// The NativeActivityContext provided by the workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Microsoft.Reliability", - "CA2000:Dispose objects before losing scope", - Justification = "Disposed in EndExecute.")] - protected override void Execute(NativeActivityContext context) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Beginning execution.", context.ActivityInstanceId)); - - string displayName = this.DisplayName; - - if (string.IsNullOrEmpty(displayName)) - displayName = this.GetType().Name; - - if (_structuredTracer.IsEnabled) - { - _structuredTracer.ActivityExecutionStarted(displayName, this.GetType().FullName); - } - - // bookmarking will only be enabled when the PSPersist variable is set to 'true' by the author. - // so we are retrieving the information before host overrides. - // if the value is false or not provided then we will not go into the unloaded mode. - - bool intBookmarking = InternalBookmarkingRequired(context); - - if (intBookmarking == false) - { - NoPersistHandle handle = this.noPersistHandle.Get(context); - handle.Enter(context); - } - this.bookmarking.Set(context, intBookmarking); - - Dictionary parameterDefaults = new Dictionary(StringComparer.OrdinalIgnoreCase); - HostParameterDefaults hostExtension = null; - - // Retrieve our host overrides - hostExtension = context.GetExtension(); - if (hostExtension != null) - { - Dictionary incomingArguments = hostExtension.Parameters; - foreach (KeyValuePair parameterDefault in incomingArguments) - { - parameterDefaults[parameterDefault.Key] = parameterDefault.Value; - } - - if (parameterDefaults.ContainsKey("PSComputerName")) - { - if (parameterDefaults["PSComputerName"] is String) - { - // If we are given a single string as a parameter default, change it to our required array - parameterDefaults["PSComputerName"] = new object[] { (string) parameterDefaults["PSComputerName"] }; - } - } - - } - - // If they haven't specified 'UseDefaultInput', ignore that host override - if( (! this.UseDefaultInput) && - parameterDefaults.ContainsKey("Input")) - { - parameterDefaults.Remove("Input"); - } - - // Store the values in context - context.SetValue>(this.ParameterDefaults, parameterDefaults); - - // Prepare our state - PSActivityContext psActivityContextInstance = new PSActivityContext(); - psActivityContextInstance.runningCommands = new Dictionary(); - psActivityContextInstance.commandQueue = new ConcurrentQueue(); - psActivityContextInstance.IsCanceled = false; - psActivityContextInstance.HostExtension = hostExtension; - - // If this is a GenericCimCmdletActivity, then copy the type of the cmdlet int0 - // the context so we'll know which type to instantiate at runtime. - GenericCimCmdletActivity genericCimActivity = this as GenericCimCmdletActivity; - if (genericCimActivity != null) - { - psActivityContextInstance.TypeImplementingCmdlet = genericCimActivity.TypeImplementingCmdlet; - } - - foreach (PSActivityArgumentInfo currentArgument in GetActivityArguments()) - { - Argument argumentInstance = currentArgument.Value; - PopulateParameterFromDefault(argumentInstance, context, currentArgument.Name, parameterDefaults); - - // Log the bound parameter - if (argumentInstance.Get(context) != null) - { - Tracer.WriteMessage( - String.Format( - CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Using parameter {1}, with value '{2}'.", - context.ActivityInstanceId, - currentArgument.Name, - argumentInstance.Get(context))); - } - } - - // Commands that get hooked up to the input stream when that - // input stream is empty should not run: - // PS > function foo { $input | Write-Output "Hello" } - // PS > foo - // PS > - PSDataCollection input = Input.Get(context); - if ((input != null) && (input.Count == 0)) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Execution skipped due to supplied (but empty) pipeline input.", context.ActivityInstanceId)); - return; - } - - // Get the output. If it's null (but has been assigned by the user), - // then populate it. - bool needToCreateOutput = false; - PSDataCollection output = Result.Get(context); - - // Set serialization properties on the stream if there is one. - // If there isn't one, the properties will be set when we - // create it. - if (output != null) - { - // Set the collection to serialize by default if that option is specified - if (!GetDisableSerialization(context)) - { - output.SerializeInput = true; - } - else - { - output.SerializeInput = false; - } - } - - if (((output == null) || (! output.IsOpen)) && (this.Result.Expression != null)) - { - if (output == null) - { - output = CreateOutputStream(context); - } - else - { - needToCreateOutput = true; - } - } - else - { - // See if it's a host default stream. If so, the expectation is that this stream will - // remain and hold all results at the end. Otherwise, clear it every invocation. - if ((ParameterDefaults != null) && - parameterDefaults.ContainsKey("Result") && - (parameterDefaults["Result"] == this.Result.Get(context))) - { - // Do nothing - } - else - { - // This is a user-supplied output stream, and should be cleared the same - // way that variables are overwritten when assigned multiple times in a - // C# method. - if (output != null) - { - bool appendOutput = false; - if ((this.AppendOutput != null) && (this.AppendOutput.Value)) - { - appendOutput = true; - } - - if (!appendOutput) - { - needToCreateOutput = true; - } - } - } - } - - // Get the error stream. If it's null (but has been assigned by the user), - // then populate it. - psActivityContextInstance.errors = PSError.Get(context); - if (this.PSError.Expression != null) - { - if ((psActivityContextInstance.errors == null) || psActivityContextInstance.errors.IsAutoGenerated) - { - psActivityContextInstance.errors = new PSDataCollection(); - psActivityContextInstance.errors.IsAutoGenerated = true; - - this.PSError.Set(context, psActivityContextInstance.errors); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: No ErrorStream was passed in; creating a new stream.", - context.ActivityInstanceId)); - } - } - - // Merge error stream to the output stream - if (this.MergeErrorToOutput.Get(context) != null && - this.MergeErrorToOutput.Get(context).GetValueOrDefault(false) && - output != null && psActivityContextInstance.errors != null) - { - // See if it's a host default stream. If so, we need to create our own instance - // to hold the error, so that the host won't consume the event first - if (ParameterDefaults != null && - parameterDefaults.ContainsKey("PSError") && - (parameterDefaults["PSError"] == PSError.Get(context))) - { - psActivityContextInstance.errors = new PSDataCollection(); - psActivityContextInstance.errors.IsAutoGenerated = true; - - this.PSError.Set(context, psActivityContextInstance.errors); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Merge error to the output stream and current error stream is the host default; creating a new stream.", - context.ActivityInstanceId)); - } - - psActivityContextInstance.MergeErrorToOutput = true; - } - - // Get the progress stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection progress = PSProgress.Get(context); - if (this.PSProgress.Expression != null) - { - if ((progress == null) || progress.IsAutoGenerated) - { - progress = new PSDataCollection(); - progress.IsAutoGenerated = true; - - this.PSProgress.Set(context, progress); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No ProgressStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - psActivityContextInstance.progress = progress; - - // Write the Activity Starting progress record... - WriteProgressRecord(context, progress, Resources.RunningString, ProgressRecordType.Processing); - - // Get the verbose stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection verbose = PSVerbose.Get(context); - if (this.PSVerbose.Expression != null) - { - if ((verbose == null) || verbose.IsAutoGenerated) - { - verbose = new PSDataCollection(); - verbose.IsAutoGenerated = true; - - this.PSVerbose.Set(context, verbose); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No VerboseStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - // Get the debug stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection debug = PSDebug.Get(context); - if (this.PSDebug.Expression != null) - { - if ((debug == null) || debug.IsAutoGenerated) - { - debug = new PSDataCollection(); - debug.IsAutoGenerated = true; - - this.PSDebug.Set(context, debug); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No DebugStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - // Get the warning stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection warning = PSWarning.Get(context); - if (this.PSWarning.Expression != null) - { - if ((warning == null) || warning.IsAutoGenerated) - { - warning = new PSDataCollection(); - warning.IsAutoGenerated = true; - - this.PSWarning.Set(context, warning); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No WarningStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - // Get the information stream. If it's null (but has been assigned by the user), - // then populate it. - PSDataCollection information = PSInformation.Get(context); - if (this.PSInformation.Expression != null) - { - if ((information == null) || information.IsAutoGenerated) - { - information = new PSDataCollection(); - information.IsAutoGenerated = true; - - this.PSInformation.Set(context, information); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No InformationStream was passed in; creating a new stream.", context.ActivityInstanceId)); - } - } - - // Clear the input stream so that it is consumed, but only if streamed from - // the host. We don't want to clear user-supplied streams. - input = Input.Get(context); - if (input != null) - { - bool usedDefaultOutput = false; - - if ((ParameterDefaults != null) && - parameterDefaults.ContainsKey("Input") && - (parameterDefaults["Input"] == this.Input.Get(context))) - { - usedDefaultOutput = true; - } - - if (usedDefaultOutput) - { - PSDataCollection newInput = new PSDataCollection(input); - newInput.IsAutoGenerated = true; - - this.Input.Set(context, newInput); - input.Clear(); - - input = Input.Get(context); - } - } - - // Auto reconnect feature is enabled only for the remoting activity with persistence - bool enableAutoConnectToManagedNode = this is PSRemotingActivity && intBookmarking; - var hostParamValues = context.GetExtension(); - bool remoteActivityResumeOperation = false; - PSWorkflowRemoteActivityState remoteActivityState = null; - if (enableAutoConnectToManagedNode && hostParamValues != null && hostParamValues.RemoteActivityState != null) - { - remoteActivityResumeOperation = hostParamValues.RemoteActivityState.RemoteActivityResumeRequired(this, false); - remoteActivityState = hostParamValues.RemoteActivityState; - } - - // Prepare the command(s) - List commandsToRun = GetTasks(context); - int taskId = 1; - foreach (ActivityImplementationContext commandToRun in commandsToRun) - { - if (remoteActivityState != null) - { - if (remoteActivityResumeOperation == true) - { - // task state is maintained in remoteActivityState as - // state value can be NotStarted, RunspaceId (running), Completed - - object taskEntry = null; - taskEntry = remoteActivityState.GetRemoteActivityRunspaceEntry(this.Id, taskId); - - if (taskEntry != null) - { - // re-execution of completed tasks is not required during the resume operation - string taskCompletion = taskEntry as string; - if ((taskCompletion != null) && (String.Compare(taskCompletion, "completed", StringComparison.OrdinalIgnoreCase) == 0)) - { - taskId++; - continue; - } - - if (taskEntry is Guid) - commandToRun.DisconnectedRunspaceInstanceId = (Guid)taskEntry; - } - } - else - { - // After crash/shutdown, this entry is required for checking all activity level fanout tasks are completed - remoteActivityState.SetRemoteActivityRunspaceEntry(this.Id, taskId, "notstarted", - commandToRun.ConnectionInfo != null ? commandToRun.ConnectionInfo.ComputerName: null); - } - } - - - // If the activity contains a ScriptBlock, then we set the user variables from the workflow. - bool hasScriptBlock = false; - - foreach (PropertyInfo field in this.GetType().GetProperties()) - { - // See if it's an argument - if (typeof(Argument).IsAssignableFrom(field.PropertyType)) - { - // Get the argument - Argument currentArgument = (Argument)field.GetValue(this, null); - - if (currentArgument != null && - (currentArgument.ArgumentType.IsAssignableFrom(typeof(ScriptBlock)) || - currentArgument.ArgumentType.IsAssignableFrom(typeof(ScriptBlock[])))) - { - hasScriptBlock = true; - break; - } - } - } - - // Populate the user variables for locally running activities or activities with scriptblock - if (hasScriptBlock || !(this is PSRemotingActivity)) - { - PopulateRunspaceFromContext(commandToRun, psActivityContextInstance, context); - } - - commandToRun.Id = taskId++; - commandToRun.EnableRemotingActivityAutoResume = enableAutoConnectToManagedNode; - - // If user execute activity with credential but no computername/connectionUri, credential will be bypassed. - // We throw a warning here. - if ((commandToRun.PSCredential != null) && - !(((commandToRun.PSComputerName != null) && !commandToRun.PSComputerName.All(name => string.IsNullOrEmpty(name))) || - ((commandToRun.PSConnectionUri != null) && (commandToRun.PSConnectionUri.Length > 0)))) - - { - commandToRun.PSWarning.Add(new WarningRecord(Resources.CredentialParameterAssignedWithNoComputerName)); - } - psActivityContextInstance.commandQueue.Enqueue(commandToRun); - } - - // Launch our delegate to actually run the given command, - // possibly across many machines. - //Func, PSDataCollection, PSActivityContext, string, - // PSWorkflowHost, bool, Dictionary, Type, PrepareSessionDelegate, object, bool> RunCommandsDelegate = Execute; - - // Set up the max running time trigger - uint? maxRunningTime = PSActionRunningTimeoutSec.Get(context); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Max running time: {1}.", context.ActivityInstanceId, maxRunningTime)); - - if (maxRunningTime.HasValue) - { - psRunningTimeoutDelayActivityInstanceVar.Set(context, context.ScheduleActivity(cancelTimer, MaxRunTimeElapsed)); - } - - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Invoking command.", context.ActivityInstanceId)); - - OnActivityCreated(this, new ActivityCreatedEventArgs(null)); - - uint? connectionRetryCount = null; - uint? connectionRetryInterval = null; - - IImplementsConnectionRetry implementsRetry = this as IImplementsConnectionRetry; - if (implementsRetry != null) - { - connectionRetryCount = implementsRetry.PSConnectionRetryCount.Get(context); - connectionRetryInterval = implementsRetry.PSConnectionRetryIntervalSec.Get(context); - } - - List modulesToLoad = new List(); - if (!string.IsNullOrEmpty(PSDefiningModule)) - { - modulesToLoad.Add(PSDefiningModule); - } - string[] requiredModules = PSRequiredModules.Get(context); - if (requiredModules != null) - { - modulesToLoad.AddRange(requiredModules); - } - - Action activateDelegate = null; - if ((hostExtension != null) && (hostExtension.ActivateDelegate != null)) - { - activateDelegate = hostExtension.ActivateDelegate; - } - - Guid jobInstanceId = Guid.Empty; - object asyncState = null; - WaitCallback callback = null; - Bookmark bookmark = context.CreateBookmark(PSBookmarkPrefix + Guid.NewGuid().ToString(), this.OnResumeBookmark); - - if (activateDelegate != null) - { - asyncState = bookmark; - callback = new WaitCallback(activateDelegate); - jobInstanceId = hostExtension.JobInstanceId; - - if ((hostExtension != null) && (hostExtension.AsyncExecutionCollection != null)) - { - Dictionary asyncExecutionCollection = null; - asyncExecutionCollection = hostExtension.AsyncExecutionCollection; - if (asyncExecutionCollection != null) - { - if (asyncExecutionCollection.ContainsKey(context.ActivityInstanceId)) - { - asyncExecutionCollection.Remove(context.ActivityInstanceId); - } - - asyncExecutionCollection.Add(context.ActivityInstanceId, psActivityContextInstance); - } - } - } - else - { - PSWorkflowInstanceExtension extension = context.GetExtension(); - BookmarkContext resumeContext = new BookmarkContext - { - CurrentBookmark = bookmark, - BookmarkResumingExtension = extension - }; - - callback = OnComplete; - asyncState = resumeContext; - } - - psActivityContextImplementationVariable.Set(context, psActivityContextInstance); - - psActivityContextInstance.Callback = callback; - psActivityContextInstance.AsyncState = asyncState; - psActivityContextInstance.JobInstanceId = jobInstanceId; - - psActivityContextInstance.ActivityParams = new ActivityParameters(connectionRetryCount, connectionRetryInterval, - PSActionRetryCount.Get(context), PSActionRetryIntervalSec.Get(context), - modulesToLoad.ToArray()); - psActivityContextInstance.Input = input; - - if (needToCreateOutput) - { - output = CreateOutputStream(context); - } - - psActivityContextInstance.Output = output; - psActivityContextInstance.WorkflowHost = GetWorkflowHost(hostExtension); - psActivityContextInstance.RunInProc = GetRunInProc(context); - psActivityContextInstance.ParameterDefaults = parameterDefaults; - psActivityContextInstance.ActivityType = GetType(); - psActivityContextInstance.PrepareSession = PrepareSession; - psActivityContextInstance.ActivityObject = this; - - if (IsActivityInlineScript(this) && RunWithCustomRemoting(context)) - { - psActivityContextInstance.RunWithCustomRemoting = true; - } - - // One more time writing the parameter defaults into the context - context.SetValue>(this.ParameterDefaults, parameterDefaults); - - // Execution - psActivityContextInstance.Execute(); - - if (_structuredTracer.IsEnabled) - { - _structuredTracer.ActivityExecutionFinished(displayName); - } - - } - - private bool GetDisableSerialization(NativeActivityContext context) - { - // First check parameter override - bool valueByParameter = PSDisableSerialization.Get(context).GetValueOrDefault(false); - if (valueByParameter) { return valueByParameter; } - - // Next check preference variable - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (string.Equals(property.DisplayName, "PSDisableSerializationPreference", StringComparison.OrdinalIgnoreCase)) - { - object serializationPreference = property.GetValue(context.DataContext); - if (serializationPreference != null) - { - bool variableValue; - if (LanguagePrimitives.TryConvertTo(serializationPreference, CultureInfo.InvariantCulture, out variableValue)) - { - return variableValue; - } - } - } - } - - // Finally, check URI setting. - // Always say "disable serialization" for the Server Manager endpoint. - // This should NOT be removed at the same time as any RDS changes and should be - // evaluated separately. - if (PSSessionConfigurationData.IsServerManager) - { - return true; - } - - return false; - } - - private bool InternalBookmarkingRequired(NativeActivityContext context) - { - bool? activityPersistFlag = GetActivityPersistFlag(context); - return activityPersistFlag.HasValue ? (bool)activityPersistFlag : false; - } - - private void MaxRunTimeElapsed(NativeActivityContext context, ActivityInstance instance) - { - if (instance.State != ActivityInstanceState.Canceled) - { - string message = String.Format(System.Globalization.CultureInfo.CurrentCulture, - Resources.RunningTimeExceeded, PSActionRunningTimeoutSec.Get(context)); - throw new TimeoutException(message); - } - } - - private PSDataCollection CreateOutputStream(NativeActivityContext context) - { - PSDataCollection output = new PSDataCollection(); - output.IsAutoGenerated = true; - output.EnumeratorNeverBlocks = true; - - // Set the collection to serialize by default if that option is specified - if (!GetDisableSerialization(context)) - { - output.SerializeInput = true; - } - else - { - output.SerializeInput = false; - } - - this.Result.Set(context, output); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: No OutputStream was passed in; creating a new stream.", context.ActivityInstanceId)); - - return output; - } - - /// - /// Write a progress record fo the current activity - /// - /// Workflow engine context - /// The progress stream to write to - /// The status string to display - /// the Progress record type - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] - protected void WriteProgressRecord(NativeActivityContext context, PSDataCollection progress, string statusDescription, ProgressRecordType type) - { - if (progress == null) - { - // While it seems like we should throw and exception, since we want to run in a non-M3P - // environment, we need to silently ignore this.... - return; - } - - string activityProgMsg = null; - - if ((context != null) && (this.PSProgressMessage != null)) - { - activityProgMsg = this.PSProgressMessage.Get(context); - - // there is no need to write the progress message since the value of psprogressmessage is explicitly provided with null - if (this.PSProgressMessage.Expression != null && string.IsNullOrEmpty(activityProgMsg)) - return; - } - - - string progressActivity; - - if (activityProgMsg == null) - { - progressActivity = this.DisplayName; - - if (string.IsNullOrEmpty(progressActivity)) - progressActivity = this.GetType().Name; - } - else - { - progressActivity = this.DisplayName + ": " + activityProgMsg; - } - - ProgressRecord pr = new ProgressRecord(0, progressActivity, statusDescription); - pr.RecordType = type; - - string currentOperation = this.Id + ":"; - - // Add in position information from the host override - if (context != null) - { - HostParameterDefaults hostExtension = context.GetExtension(); - if (hostExtension != null) - { - HostSettingCommandMetadata commandMetadata = hostExtension.HostCommandMetadata; - if (commandMetadata != null) - { - currentOperation += String.Format(CultureInfo.CurrentCulture, - Resources.ProgressPositionMessage, - commandMetadata.CommandName, commandMetadata.StartLineNumber, commandMetadata.StartColumnNumber); - } - } - } - - pr.CurrentOperation = currentOperation; - - // Look to see if the parent id has been set for this scope... - if (context != null) - { - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (string.Equals(property.DisplayName, WorkflowPreferenceVariables.PSParentActivityId, StringComparison.OrdinalIgnoreCase)) - { - object parentId = property.GetValue(context.DataContext); - if (parentId != null) - { - int idToUse; - if (LanguagePrimitives.TryConvertTo(parentId, CultureInfo.InvariantCulture, out idToUse)) - { - pr.ParentActivityId = idToUse; - } - } - } - else if (string.Equals(property.DisplayName, "ProgressPreference", StringComparison.OrdinalIgnoreCase)) - { - string progressPreference = property.GetValue(context.DataContext) as string; - if (!string.IsNullOrEmpty(progressPreference) && - (string.Equals(progressPreference, "SilentlyContinue", StringComparison.OrdinalIgnoreCase) || - string.Equals(progressPreference, "Ignore", StringComparison.OrdinalIgnoreCase))) - { - // See if we should skip writing out the progress record... - return; - } - } - } - } - - progress.Add(pr); - } - - - private static void OnComplete(object state) - { - _structuredTracer.Correlate(); - - //BookmarkContext bookmarkContext = (BookmarkContext)result.AsyncState; - Dbg.Assert(state != null, "State not passed correctly to OnComplete"); - BookmarkContext bookmarkContext = state as BookmarkContext; - Dbg.Assert(bookmarkContext != null, "BookmarkContext not passed correctly to OnComplete"); - PSWorkflowInstanceExtension extension = bookmarkContext.BookmarkResumingExtension; - Bookmark bookmark = bookmarkContext.CurrentBookmark; - - // Resuming the bookmark - ThreadPool.QueueUserWorkItem(o => extension.BeginResumeBookmark(bookmark, null, ar => extension.EndResumeBookmark(ar), null)); - - } - - private void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object value) - { - _structuredTracer.Correlate(); - - if (this.bookmarking.Get(context) == false) - { - NoPersistHandle handle = this.noPersistHandle.Get(context); - handle.Exit(context); - } - - //context.RemoveBookmark(bookmark); - - // Check for the activity if it is restarting - // this is possible when the activity in bookmarked stated get crashed/terminated - // in this case we restart this activity - ActivityOnResumeAction action = ActivityOnResumeAction.Resume; - if (value != null && value.GetType() == typeof(ActivityOnResumeAction)) - action = (ActivityOnResumeAction) value; - - // All activity level fanout tasks/commands might have finished before the process crash for the remoting activity - // Check activity restart is not required. - var hostParamValues = context.GetExtension(); - if (this is PSRemotingActivity && hostParamValues != null && hostParamValues.RemoteActivityState != null) - { - // Auto reconnect is enabled for remoting activity with PSPersist value true - if (InternalBookmarkingRequired(context)) - { - bool remoteActivityCompleted = !(hostParamValues.RemoteActivityState.RemoteActivityResumeRequired(this, true)); - - if (remoteActivityCompleted == true) - action = ActivityOnResumeAction.Resume; - } - } - - if (action == ActivityOnResumeAction.Restart) - { - this.Execute(context); - return; - } - - // this is expected when there would be a disconnected execution - // here we expects the PSActivityHostArguments to be passed - // argument contains the result from the execution - PSResumableActivityContext arguments = null; - if (value != null && value.GetType() == typeof(PSResumableActivityContext)) - { - arguments = (PSResumableActivityContext)value; - } - if (arguments != null) - { - HostParameterDefaults hostValues = context.GetExtension(); - if (hostValues != null) - { - if (arguments.SupportDisconnectedStreams && arguments.Streams != null) - { - PopulateSteamsData(arguments, context, hostValues); - } - - PSDataCollection psprogress = null; - if (this.PSProgress.Expression != null) - { - psprogress = PSProgress.Get(context); - } - else - { - if (hostValues.Parameters["PSProgress"] != null && hostValues.Parameters["PSProgress"].GetType() == typeof(PSDataCollection)) - { - psprogress = hostValues.Parameters["PSProgress"] as PSDataCollection; - } - } - - // If we got an exception, throw it here. - if (arguments.Error != null) - { - WriteProgressRecord(context, psprogress, Resources.FailedString, ProgressRecordType.Completed); - - Tracer.WriteMessage("PSActivity", "OnResumeBookmark", context.WorkflowInstanceId, - @"We are about to rethrow the exception in order to preserve the stack trace writing it into the logs."); - Tracer.TraceException(arguments.Error); - Dbg.Assert(String.IsNullOrWhiteSpace(arguments.Error.Message) == false, "Exception to be thrown doesnt have proper error message, throwing this will results in Suspended job state instead of Failed state !"); - throw arguments.Error; - } - - // Perform persistence - this.ActivityEndPersistence(context); - - - // Write the activity completed progress record - if (arguments.Failed) - { - WriteProgressRecord(context, psprogress, Resources.FailedString, ProgressRecordType.Completed); - } - else - { - WriteProgressRecord(context, psprogress,Resources.CompletedString, ProgressRecordType.Completed); - } - } - - return; - } - - // In the most of the cases where the state is null - PSActivityContext psActivityContextInstance = null; - PSDataCollection progress = null; - - try - { - if (this.bookmarking.Get(context) == false) - { - progress = PSProgress.Get(context); - psActivityContextInstance = psActivityContextImplementationVariable.Get(context); - - //TODO: add below block in finally for whole function - psActivityContextImplementationVariable.Set(context, null); - - HostParameterDefaults hostValues = context.GetExtension(); - if (hostValues != null) - { - - if ((hostValues != null) && (hostValues.AsyncExecutionCollection != null)) - { - Dictionary asyncExecutionCollection = null; - asyncExecutionCollection = hostValues.AsyncExecutionCollection; - if (asyncExecutionCollection != null) - { - if (asyncExecutionCollection.ContainsKey(context.ActivityInstanceId)) - { - asyncExecutionCollection.Remove(context.ActivityInstanceId); - } - } - } - } - - - } - else - { - HostParameterDefaults hostValues = context.GetExtension(); - if (hostValues != null) - { - - if ((hostValues != null) && (hostValues.AsyncExecutionCollection != null)) - { - Dictionary asyncExecutionCollection = null; - asyncExecutionCollection = hostValues.AsyncExecutionCollection; - if (asyncExecutionCollection != null) - { - if (asyncExecutionCollection.ContainsKey(context.ActivityInstanceId)) - { - psActivityContextInstance = asyncExecutionCollection[context.ActivityInstanceId]; - asyncExecutionCollection.Remove(context.ActivityInstanceId); - } - } - } - } - - if (psActivityContextInstance != null) - { - progress = psActivityContextInstance.progress; - - if (this.Result.Expression != null) - { - this.Result.Set(context, psActivityContextInstance.Output); - } - } - } - - - // Kill the MaxRunningTime timer - var runningCancelTimerActivityInstance = psRunningTimeoutDelayActivityInstanceVar.Get(context); - if (runningCancelTimerActivityInstance != null) - { - psRunningTimeoutDelayActivityInstanceVar.Set(context, null); - context.CancelChild(runningCancelTimerActivityInstance); - } - - if (psActivityContextInstance != null) - { - // If we got an exception, throw it here. - if (psActivityContextInstance.exceptions.Count > 0) - { - WriteProgressRecord(context, progress, Resources.FailedString, ProgressRecordType.Completed); - - Tracer.WriteMessage("PSActivity", "OnResumeBookmark", context.WorkflowInstanceId, - @"We are about to rethrow the exception in order to preserve the stack trace writing it into the logs."); - Tracer.TraceException(psActivityContextInstance.exceptions[0]); - - Dbg.Assert(String.IsNullOrWhiteSpace(psActivityContextInstance.exceptions[0].Message) == false, "Exception to be thrown doesnt have proper error message, throwing this will results in Suspended job state instead of Failed state !"); - throw psActivityContextInstance.exceptions[0]; - } - } - - // Perform persistence - this.ActivityEndPersistence(context); - - - // Write the activity completed progress record - if (psActivityContextInstance != null && psActivityContextInstance.Failed) - { - WriteProgressRecord(context, progress, Resources.FailedString, ProgressRecordType.Completed); - } - else - { - WriteProgressRecord(context, progress, Resources.CompletedString, ProgressRecordType.Completed); - } - } - finally - { - if (psActivityContextInstance != null) - { - // If we had an error to suspend on, schedule the suspend activity - if (psActivityContextInstance.SuspendOnError) - { - context.ScheduleActivity(this.suspendActivity); - } - - psActivityContextInstance.Dispose(); - psActivityContextInstance = null; - } - } - } - - /// - /// The method is override-able by the derived classes in case they would like to implement different logic at the end of persistence. - /// The default behavior would be to schedule the 'Persist' activity if the PSPersist flag is true or Host is asking for it. - /// - /// The native activity context of execution engine. - protected virtual void ActivityEndPersistence(NativeActivityContext context) - { - bool? activityPersist = GetActivityPersistFlag(context); - - bool? HostPSPersistVariable = null; - if (this.PSPersist.Expression == null && this.PSPersist.Get(context).HasValue) - { - HostPSPersistVariable = this.PSPersist.Get(context).Value; - } - - bool hostPersist = false; - hostPersist = this.GetHostPersistFlag(context); - - // Schedule Persist activity if the preference variable was true, the activity explicitly requests it - // or the host has requested persistence. - if (((activityPersist == null) && (HostPSPersistVariable == true)) || hostPersist == true || activityPersist == true) - { - string bookmarkname = PSActivity.PSPersistBookmarkPrefix + Guid.NewGuid().ToString().Replace("-", "_"); - context.CreateBookmark(bookmarkname, BookmarkResumed); - } - } - private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value) - { - } - - - private bool GetHostPersistFlag(NativeActivityContext context) - { - Func hostDelegate = null; - - HostParameterDefaults hostValues = context.GetExtension(); - - if (hostValues != null) - { - if ((hostValues != null) && (hostValues.HostPersistenceDelegate != null)) - { - hostDelegate = hostValues.HostPersistenceDelegate; - } - } - - if (hostDelegate == null) - { - return false; - } - - bool value = hostDelegate(); - - return value; - } - private bool? GetActivityPersistFlag(NativeActivityContext context) - { - bool? activityPersist = null; - if (this.PSPersist.Expression != null && this.PSPersist.Get(context).HasValue) - { - activityPersist = this.PSPersist.Get(context).Value; - } - else - { - // Look to see if there is a PSPersistPreference variable in scope... - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (string.Equals(property.DisplayName, WorkflowPreferenceVariables.PSPersistPreference, StringComparison.OrdinalIgnoreCase)) - { - object variableValue = property.GetValue(context.DataContext); - if (variableValue != null) - { - bool persist; - if (LanguagePrimitives.TryConvertTo(variableValue, CultureInfo.InvariantCulture, out persist)) - { - activityPersist = persist; - } - } - } - } - } - - // if activity level PSPersist value OR $PSPersistPreference are not found try to get the PSPersist value specified at workflow level - if (activityPersist == null) - { - HostParameterDefaults hostExtension = context.GetExtension(); - if (hostExtension != null) - { - Dictionary incomingArguments = hostExtension.Parameters; - if (incomingArguments.ContainsKey(Constants.Persist)) - { - activityPersist = (bool)incomingArguments[Constants.Persist]; - } - } - } - return activityPersist; - } - - private void PopulateSteamsData(PSResumableActivityContext arguments, NativeActivityContext context, HostParameterDefaults hostValues) - { - // setting the output from arguments - if (arguments.Streams.OutputStream != null) - { - if (this.Result.Expression != null) - { - this.Result.Set(context, arguments.Streams.OutputStream); - } - else - { - if (hostValues.Parameters["Result"] != null && hostValues.Parameters["Result"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection output = hostValues.Parameters["Result"] as PSDataCollection; - if (output != arguments.Streams.OutputStream && output != null && output.IsOpen) - { - foreach (PSObject obj in arguments.Streams.OutputStream) - { - output.Add(obj); - } - } - } - } - } - - // setting the input from the arguments - if (arguments.Streams.InputStream != null) - { - if (this.Input.Expression != null) - { - this.Input.Set(context, arguments.Streams.InputStream); - } - else - { - if (hostValues.Parameters["Input"] != null && hostValues.Parameters["Input"].GetType() == typeof(PSDataCollection)) - { - hostValues.Parameters["Input"] = arguments.Streams.InputStream; - } - } - } - - // setting the error from arguments - if (arguments.Streams.ErrorStream != null) - { - if (this.PSError.Expression != null) - { - this.PSError.Set(context, arguments.Streams.ErrorStream); - } - else - { - if (hostValues.Parameters["PSError"] != null && hostValues.Parameters["PSError"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection error = hostValues.Parameters["PSError"] as PSDataCollection; - if (error != arguments.Streams.ErrorStream && error != null && error.IsOpen) - { - foreach (ErrorRecord obj in arguments.Streams.ErrorStream) - { - error.Add(obj); - } - } - } - } - } - - // setting the warning from arguments - if (arguments.Streams.WarningStream != null) - { - if (this.PSWarning.Expression != null) - { - this.PSWarning.Set(context, arguments.Streams.WarningStream); - } - else - { - if (hostValues.Parameters["PSWarning"] != null && hostValues.Parameters["PSWarning"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection warning = hostValues.Parameters["PSWarning"] as PSDataCollection; - if (warning != arguments.Streams.WarningStream && warning != null && warning.IsOpen) - { - foreach (WarningRecord obj in arguments.Streams.WarningStream) - { - warning.Add(obj); - } - } - } - } - } - // setting the progress from arguments - if (arguments.Streams.ProgressStream != null) - { - if (this.PSProgress.Expression != null) - { - this.PSProgress.Set(context, arguments.Streams.ProgressStream); - } - else - { - if (hostValues.Parameters["PSProgress"] != null && hostValues.Parameters["PSProgress"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection tmpProgress = hostValues.Parameters["PSProgress"] as PSDataCollection; - if (tmpProgress != arguments.Streams.ProgressStream && tmpProgress != null && tmpProgress.IsOpen) - { - foreach (ProgressRecord obj in arguments.Streams.ProgressStream) - { - tmpProgress.Add(obj); - } - } - } - } - } - - // setting the verbose from arguments - if (arguments.Streams.VerboseStream != null) - { - if (this.PSVerbose.Expression != null) - { - this.PSVerbose.Set(context, arguments.Streams.VerboseStream); - } - else - { - if (hostValues.Parameters["PSVerbose"] != null && hostValues.Parameters["PSVerbose"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection verbose = hostValues.Parameters["PSVerbose"] as PSDataCollection; - if (verbose != arguments.Streams.VerboseStream && verbose != null && verbose.IsOpen) - { - foreach (VerboseRecord obj in arguments.Streams.VerboseStream) - { - verbose.Add(obj); - } - } - } - } - } - // setting the debug from arguments - if (arguments.Streams.DebugStream != null) - { - if (this.PSDebug.Expression != null) - { - this.PSDebug.Set(context, arguments.Streams.DebugStream); - } - else - { - if (hostValues.Parameters["PSDebug"] != null && hostValues.Parameters["PSDebug"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection debug = hostValues.Parameters["PSDebug"] as PSDataCollection; - if (debug != arguments.Streams.DebugStream && debug != null && debug.IsOpen) - { - foreach (DebugRecord obj in arguments.Streams.DebugStream) - { - debug.Add(obj); - } - } - } - } - } - - // setting the information stream from arguments - if (arguments.Streams.InformationStream != null) - { - if (this.PSInformation.Expression != null) - { - this.PSInformation.Set(context, arguments.Streams.InformationStream); - } - else - { - if (hostValues.Parameters["PSInformation"] != null && hostValues.Parameters["PSInformation"].GetType() == typeof(PSDataCollection)) - { - PSDataCollection information = hostValues.Parameters["PSInformation"] as PSDataCollection; - if (information != arguments.Streams.InformationStream && information != null && information.IsOpen) - { - foreach (InformationRecord obj in arguments.Streams.InformationStream) - { - information.Add(obj); - } - } - } - } - } - } - - /// - /// Populates the runspace with user variables from the current context - /// - /// - /// - /// - private void PopulateRunspaceFromContext(ActivityImplementationContext implementationContext, PSActivityContext activityContext, NativeActivityContext context) - { - System.Diagnostics.Debug.Assert(implementationContext != null, "Implementation context cannot be null"); - - if (implementationContext.PowerShellInstance != null) - { - // Then, set the variables from the workflow - SetActivityVariables(context, activityContext.UserVariables); - } - } - - /// - /// Populates the required variables to set in runspace - /// - /// - /// - internal void SetActivityVariables(NativeActivityContext context, Dictionary activityVariables) - { - Dictionary defaults = this.ParameterDefaults.Get(context); - - string[] streams = - { - "Result", "PSError", "PSWarning", "PSVerbose", "PSDebug", "PSProgress", "PSInformation" - }; - - // First, set the variables from the user's variables - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (String.Equals(property.Name, "ParameterDefaults", StringComparison.OrdinalIgnoreCase)) - continue; - - Object value = property.GetValue(context.DataContext); - if (value != null) - { - object tempValue = value; - - PSDataCollection collectionObject = value as PSDataCollection; - - if (collectionObject != null && collectionObject.Count == 1) - { - tempValue = collectionObject[0]; - } - - activityVariables[property.Name] = tempValue; - } - } - - // Then, set anything we received from parameters - foreach (PSActivityArgumentInfo currentArgument in GetActivityArguments()) - { - string @default = currentArgument.Name; - if (streams.Any(item => string.Equals(item, @default, StringComparison.OrdinalIgnoreCase))) - continue; - - object argumentValue = currentArgument.Value.Get(context); - if (argumentValue != null && !activityVariables.ContainsKey(currentArgument.Name)) - { - activityVariables[currentArgument.Name] = argumentValue; - } - } - - // Then, set the variables from the host defaults - if (defaults != null) - { - foreach (string hostDefault in defaults.Keys) - { - string @default = hostDefault; - if (streams.Any(item => string.Equals(item, @default, StringComparison.OrdinalIgnoreCase))) - continue; - - object propertyValue = defaults[hostDefault]; - if (propertyValue != null && !activityVariables.ContainsKey(hostDefault)) - { - activityVariables[hostDefault] = propertyValue; - } - } - } - } - - /// - /// Populates a parameter from the defaults supplied by the workflow host - /// - /// The argument to modify (i.e.: Input, Result, ComputerName, etc) - /// The activity context to use - /// The name of the argument - /// The parameter defaults - private void PopulateParameterFromDefault(Argument argument, NativeActivityContext context, string argumentName, Dictionary parameterDefaults) - { - // See if they haven't specified a value - if ((argument != null) && - (argument.Expression == null) && - (argument.Direction != ArgumentDirection.Out)) - { - // See if we have a parameter defaults collection - if (ParameterDefaults != null) - { - // See if it has this parameter - if (parameterDefaults.ContainsKey(argumentName)) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Using default {1} value.", context.ActivityInstanceId, argumentName)); - Object parameterDefault = parameterDefaults[argumentName]; - - // Map any switch parameters to booleans if required - if ((argument.ArgumentType == typeof(Boolean)) || - (argument.ArgumentType == typeof(Nullable))) - { - if (parameterDefault is SwitchParameter) - { - parameterDefault = ((SwitchParameter) parameterDefault).ToBool(); - } - } - - // If the argument type is nullable, but the provided default - // is a non-nullable version, create a nullable version of it - // to set. - if ((argument.ArgumentType.IsGenericType) && - (argument.ArgumentType.GetGenericTypeDefinition() == typeof(Nullable<>)) && - (!(parameterDefault is Nullable))) - { - parameterDefault = LanguagePrimitives.ConvertTo(parameterDefault, - argument.ArgumentType, - CultureInfo.InvariantCulture); - } - - if (argument.ArgumentType.IsAssignableFrom(typeof(PSCredential)) && - parameterDefault.GetType().IsAssignableFrom(typeof(PSObject))) - { - parameterDefault = LanguagePrimitives.ConvertTo(parameterDefault, - typeof(PSCredential), CultureInfo.InvariantCulture); - } - - argument.Set(context, parameterDefault); - } - } - } - } - - // Populates the activity task variables from the current PSActivity instance - private void PopulateActivityImplementationContext(ActivityImplementationContext implementationContext, NativeActivityContext context, int index) - { - // Go through all of the public PSActivity arguments, and populate their value - // into the ActivityImplementationContext. - foreach (PSActivityArgumentInfo field in GetActivityArguments()) - { - // Get the field of the same name from the 'ActivityImplementationContext' class - PropertyInfo implementationContextField = implementationContext.GetType().GetProperty(field.Name); - if (implementationContextField == null) - { - // If you have added a new argument then please added it to 'ActivityImplementationContext' classs. - throw new Exception("Could not find corresponding task context field for activity argument: " + field.Name); - } - - if (string.Equals(field.Name, PSComputerName, StringComparison.OrdinalIgnoreCase) && index != -1) - { - // set only the corresponding entry for computername - PopulatePSComputerName(implementationContext, context, field, index); - } - else - { - // And set it in the task context - implementationContextField.SetValue(implementationContext, field.Value.Get(context), null); - } - } - } - - private const string PSComputerName = "PSComputerName"; - private static void PopulatePSComputerName(ActivityImplementationContext implementationContext, NativeActivityContext context, PSActivityArgumentInfo field, int index) - { - Dbg.Assert(string.Equals(field.Name, PSComputerName, StringComparison.OrdinalIgnoreCase), "PSActivityArgumentInfo passed should be for PSComputerName"); - - PropertyInfo computerNameField = implementationContext.GetType().GetProperty(field.Name); - string[] computerNames = (string[]) field.Value.Get(context); - - computerNameField.SetValue(implementationContext, new string[]{computerNames[index]}, null); - } - - /// - /// Overload this method to implement any command-type specific preparations. - /// If this command needs any workflow-specific information during its PrepareSession call, - /// it should be stored in ActivityImplementationContext.WorkflowContext. - /// - /// The activity context to use - protected virtual List GetImplementation(NativeActivityContext context) - { - ActivityImplementationContext implementationContext = GetPowerShell(context); - UpdateImplementationContextForLocalExecution(implementationContext, context); - - return new List() { implementationContext }; - } - - /// - /// Updates the ImplementationContext returned from GetPowerShell() to support local execution - /// against the host's runspace pool. - /// - /// The implementation context returned by GetPowerShell() - /// The activity context to use - protected internal void UpdateImplementationContextForLocalExecution(ActivityImplementationContext implementationContext, ActivityContext context) - { - } - - - /// - /// The method for derived activities to return a configured instance of System.Management.Automation.PowerShell. - /// The implementor should have added all of the commands and parameters required to launch their command through - /// the standard AddCommand() and AddParameter() methods. Derived activities should not manage the Runspace property - /// directly, as the PSActivity class configures the runspace afterward to enable remote connections. - /// - /// The NativeActivityContext for the currently running activity. - /// A populated instance of System.Management.Automation.PowerShell - /// The infrastructure takes responsibility for closing and disposing the PowerShell instance returned. - protected abstract ActivityImplementationContext GetPowerShell(NativeActivityContext context); - - - /// - /// The method for derived activities to customize the runspace of a System.Management.Automation.PowerShell instance - /// that the runtime has prepared for them. - /// If the command needs any workflow-specific information during this PrepareSession call, - /// it should be stored in ActivityImplementationContext.WorkflowContext during the GetCommand preparation phase. - /// - /// The ActivityImplementationContext returned by the call to GetCommand. - protected virtual void PrepareSession(ActivityImplementationContext implementationContext) - { - } - - /// - /// If an activity needs to load a module before it can execute, override this member - /// to return the name of that module. - /// - protected string DefiningModule - { - get - { - return string.Empty; - } - } - - /// - /// Retrieve all of the default arguments from the type and its parents. - /// - /// All of the default arguments from the type and its parents - protected IEnumerable GetActivityArguments() - { - // Walk up the type hierarchy, looking for types that we should pull in - // parameter defaults. - Type currentType = this.GetType(); - - while (currentType != null) - { - // We don't want to support parameter defaults for arguments on - // concrete types (as they almost guaranteed to collide with other types), - // but base classes make sense. - if (currentType.IsAbstract) - { - // Populate any parameter defaults. We only look at fields that are defined on this - // specific type (as opposed to derived types) so that we don't make assumptions about - // other activities and their defaults. - foreach (PropertyInfo field in currentType.GetProperties()) - { - // See if it's an argument - if (typeof(Argument).IsAssignableFrom(field.PropertyType)) - { - // Get the argument - Argument currentArgument = (Argument)field.GetValue(this, null); - yield return new PSActivityArgumentInfo { Name = field.Name, Value = currentArgument }; - } - } - } - - // Go to our base type, but stop when we go above PSActivity - currentType = currentType.BaseType; - if (!typeof(PSActivity).IsAssignableFrom(currentType)) - currentType = null; - } - } - - // Get the tasks from the derived activity, and save the state we require. - private List GetTasks(NativeActivityContext context) - { - List tasks = GetImplementation(context); - - if (IsActivityInlineScript(this)) - { - // in case of inlinescript activity supporting custom remoting we need - // to ensure that PSComputerName is populated only for the specified - // index - if (RunWithCustomRemoting(context)) - { - for(int i=0; i - /// Indicates if preference variables need to be updated - /// - protected virtual bool UpdatePreferenceVariable - { - get { return true; } - } - - // Common configuration of a SMA.PowerShell instance. - /// - /// Add the other streams and enqueue the command to be run - /// - /// The activity context to use - /// The powershell activity context. - /// The activity type. - /// The prepare session delegate. - /// This object representing activity. - private static void UpdatePowerShell(ActivityImplementationContext implementationContext, PSActivityContext psActivityContext, Type ActivityType, PrepareSessionDelegate PrepareSession, object activityObject) - { - try - { - PrepareSession(implementationContext); - System.Management.Automation.PowerShell invoker = implementationContext.PowerShellInstance; - - // Prepare the streams - if (implementationContext.PSError != null) - { - invoker.Streams.Error = implementationContext.PSError; - } - - if (implementationContext.PSProgress != null) - { - invoker.Streams.Progress = implementationContext.PSProgress; - } - - if (implementationContext.PSVerbose != null) - { - invoker.Streams.Verbose = implementationContext.PSVerbose; - } - - if (implementationContext.PSDebug != null) - { - invoker.Streams.Debug = implementationContext.PSDebug; - } - - if (implementationContext.PSWarning != null) - { - invoker.Streams.Warning = implementationContext.PSWarning; - } - - if (implementationContext.PSInformation != null) - { - invoker.Streams.Information = implementationContext.PSInformation; - } - - // InlineScript activity needs to handle these in its own way - PSActivity activityBase = activityObject as PSActivity; - Dbg.Assert(activityBase != null, "Only activities derived from PSActivityBase are supported"); - if (activityBase.UpdatePreferenceVariable) - { - UpdatePreferenceVariables(implementationContext); - } - - //OnActivityCreated(activityObject, new ActivityCreatedEventArgs(invoker)); - } - catch (Exception e) - { - // Catch all exceptions and add them to the exception list. - // This way, they will be reported on EndExecute(), rather than - // killing the process if an exception happens on the background thread. - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(e); - } - } - } - - private static void UpdatePreferenceVariables(ActivityImplementationContext implementationContext) - { - // Update the PowerShell ubiquitous parameters - Command activityCommand = implementationContext.PowerShellInstance.Commands.Commands[0]; - - if (implementationContext.Verbose != null) - { - activityCommand.Parameters.Add("Verbose", implementationContext.Verbose); - } - - if (implementationContext.Debug != null) - { - activityCommand.Parameters.Add("Debug", implementationContext.Debug); - } - - if (implementationContext.WhatIf != null) - { - activityCommand.Parameters.Add("WhatIf", implementationContext.WhatIf); - } - - if (implementationContext.ErrorAction != null) - { - activityCommand.Parameters.Add("ErrorAction", implementationContext.ErrorAction); - } - - if (implementationContext.WarningAction != null) - { - activityCommand.Parameters.Add("WarningAction", implementationContext.WarningAction); - } - - if (implementationContext.InformationAction != null) - { - activityCommand.Parameters.Add("InformationAction", implementationContext.InformationAction); - } - } - - internal const int CommandRunInProc = 0; - internal const int RunInProcNoRunspace = 1; - internal const int CommandRunOutOfProc = 2; - internal const int CommandRunRemotely = 3; - internal const int CimCommandRunInProc = 4; - internal const int CleanupActivity = 5; - - /// - /// - /// - /// - /// THREADING CONTRACT: This function is designed to be - /// lightweight and to run on the Workflow thread when Execute() - /// is called. When any changes are made to this function the contract - /// needs to be honored - internal static void BeginRunOneCommand(RunCommandsArguments args) - { - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Beginning action to run command {0}.", commandToRun)); - - if (CheckForCancel(psActivityContext)) return; - - // initialize for command execution based on type - InitializeOneCommand(args); - - // run the command based on type - if (args.CommandExecutionType != CommandRunRemotely && - args.CommandExecutionType != CommandRunInProc && - args.CommandExecutionType != CimCommandRunInProc) - { - // when the command is run on a remote runspace it is - // executed on the callback thread from connection - // manager - BeginExecuteOneCommand(args); - } - } - } - - /// - /// Initialize a single command for execution. - /// - /// - /// THREADING CONTRACT: This function is designed to be - /// lightweight and to run on the Workflow thread when Execute() - /// is called. When any changes are made to this function the - /// contract needs to be honored - private static void InitializeOneCommand(RunCommandsArguments args) - { - ActivityParameters activityParameters = args.ActivityParameters; - PSActivityContext psActivityContext = args.PSActivityContext; - PSWorkflowHost workflowHost = args.WorkflowHost; - Dictionary parameterDefaults = args.ParameterDefaults; - Type activityType = args.ActivityType; - PrepareSessionDelegate prepareSession = args.Delegate; - object activityObject = args.ActivityObject; - ActivityImplementationContext implementationContext = args.ImplementationContext; - int commandExecutionType = args.CommandExecutionType; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Beginning initialization for command '{0}'.", commandToRun)); - // - // Common Initialization - // - - // Store which command we're running. We need to lock this one - // because Dictionary is not thread-safe (while ConcurrentQueue is, - // by definition.) - lock (psActivityContext.runningCommands) - { - if (CheckForCancel(psActivityContext)) return; - - if (!psActivityContext.runningCommands.ContainsKey(commandToRun)) - psActivityContext.runningCommands[commandToRun] = new RetryCount(); - } - - if (CheckForCancel(psActivityContext)) return; - - // Record the invocation attempt - psActivityContext.runningCommands[commandToRun].ActionAttempts++; - - // NOTE: previously UpdatePowerShell was called after the runspace - // for the command was open and available. An assumption is made - // that this is not required and hence the code is moved before - // the runspace is available. If there are any issues found flip - // the order back - - // Let the activity prepare the PowerShell instance. - if (commandExecutionType != CleanupActivity) - UpdatePowerShell(implementationContext, psActivityContext, activityType, prepareSession, - activityObject); - - // - // Initialization based on type - // - switch (commandExecutionType) - { - case CommandRunInProc: - { - // good to run on current thread - InitializeActivityEnvironmentAndAddRequiredModules(implementationContext, activityParameters); - workflowHost.LocalRunspaceProvider.BeginGetRunspace(null, 0, 0, - LocalRunspaceProviderCallback, args); - } - break; - - case RunInProcNoRunspace: - { - - // These commands don't have a runspace - // so we don't need to do anything here - } - break; - - case CimCommandRunInProc: - { - workflowHost.LocalRunspaceProvider.BeginGetRunspace(null, 0, 0, - LocalRunspaceProviderCallback, args); - } - break; - - case CommandRunOutOfProc: - { - // out of proc commands do not require the runspace in the - // powershell. However since this runspace was created by - // the local runspace provider it need to be released and - // not disposed We need to release the connection - // before calling into activity host manager so as to not - // risk closing an out of process runspace if - // things happen too quickly - - InitializeActivityEnvironmentAndAddRequiredModules(implementationContext, activityParameters); - - } - break; - case CommandRunRemotely: - { - // when a command is run remotely, the connection manager - // assigns a remote runspace. Dispose the existing one - // We should not set the runspace to null as it required in - // the callback to retrieve connection info in case there - // is a connection failure. We need to close the connection - // before calling into connection manager so as to not - // risk closing a connection manager assigned runspace if - // things happen too quickly - DisposeRunspaceInPowerShell(commandToRun, false); - - InitializeActivityEnvironmentAndAddRequiredModules(implementationContext, activityParameters); - - // can block - should be run on a different thread - WSManConnectionInfo connectionInfo = - commandToRun.Runspace.ConnectionInfo as WSManConnectionInfo; - workflowHost.RemoteRunspaceProvider.BeginGetRunspace(connectionInfo, - activityParameters.ConnectionRetryCount. - GetValueOrDefault(0), - activityParameters.ConnectionRetryInterval. - GetValueOrDefault(0), - ConnectionManagerCallback, - args); - - } - break; - - case CleanupActivity: - { - // no initialization required - } - break; - } - } - } - - private static void InitializeActivityEnvironmentAndAddRequiredModules(ActivityImplementationContext implementationContext, - ActivityParameters activityParameters) - { - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - if (implementationContext.PSActivityEnvironment == null) - implementationContext.PSActivityEnvironment = new PSActivityEnvironment(); - - PSActivityEnvironment policy = implementationContext.PSActivityEnvironment; - - foreach (string module in activityParameters.PSRequiredModules ?? new string[0]) - { - actionTracer.WriteMessage("Adding dependent module to policy: " + module); - policy.Modules.Add(module); - } - } - } - - private static void DisposeRunspaceInPowerShell(System.Management.Automation.PowerShell commandToRun, bool setToNull=true) - { - Dbg.Assert(commandToRun.Runspace != null, "Method cannot be called when runspace is null"); - commandToRun.Runspace.Dispose(); - commandToRun.Runspace.Close(); - if (setToNull) - commandToRun.Runspace = null; - } - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This function runs on the workflow thread when Execute() - /// is called for all cases except the remote runspace case - /// where it runs on a WinRM thread or the connection manager - /// servicing thread - /// Therefore this function is designed to be lightweight in - /// all cases - /// When any changes are made to this function the contract needs to - /// be honored - private static void BeginExecuteOneCommand(RunCommandsArguments args) - { - PSActivityContext psActivityContext = args.PSActivityContext; - PSWorkflowHost workflowHost = args.WorkflowHost; - ActivityImplementationContext implementationContext = args.ImplementationContext; - PSDataCollection input = args.Input; - PSDataCollection output = args.Output; - int commandExecutionType = args.CommandExecutionType; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "BEGIN BeginExecuteOneCommand {0}.", commandToRun)); - - switch (commandExecutionType) - { - case RunInProcNoRunspace: - { - // execute on threadpool thread - ThreadPool.QueueUserWorkItem(ExecuteOneRunspaceFreeCommandWorker, args); - } - break; - - case CimCommandRunInProc: - { - // execute on threadpool thread - ThreadPool.QueueUserWorkItem(InitializeRunspaceAndExecuteCommandWorker, args); - } - break; - - case CommandRunOutOfProc: - { - Dbg.Assert(implementationContext.PSActivityEnvironment != null, - "Policy should have been initialized correctly by the initialization method"); - - if (CheckForCancel(psActivityContext)) return; - - PSResumableActivityHostController resumableController = workflowHost.PSActivityHostController as PSResumableActivityHostController; - if (resumableController != null) - { - PowerShellStreams streams = new PowerShellStreams(); - - if (resumableController.SupportDisconnectedPSStreams) - { - streams.InputStream = input; - streams.OutputStream = new PSDataCollection(); - streams.ErrorStream = new PSDataCollection(); - streams.DebugStream = new PSDataCollection(); - streams.ProgressStream = new PSDataCollection(); - streams.VerboseStream = new PSDataCollection(); - streams.WarningStream = new PSDataCollection(); - streams.InformationStream = new PSDataCollection(); - } - else - { - streams.InputStream = input; - streams.OutputStream = output; - streams.DebugStream = commandToRun.Streams.Debug; - streams.ErrorStream = commandToRun.Streams.Error; - streams.ProgressStream = commandToRun.Streams.Progress; - streams.VerboseStream = commandToRun.Streams.Verbose; - streams.WarningStream = commandToRun.Streams.Warning; - streams.InformationStream = commandToRun.Streams.Information; - } - - resumableController.StartResumablePSCommand(args.PSActivityContext.JobInstanceId, - (Bookmark)args.PSActivityContext.AsyncState, - commandToRun, - streams, - implementationContext.PSActivityEnvironment, - (PSActivity)psActivityContext.ActivityObject); - } - else - { - PSOutOfProcessActivityController delegateController = workflowHost.PSActivityHostController as PSOutOfProcessActivityController; - if (delegateController != null) - { - AddHandlersToStreams(commandToRun, args); - - IAsyncResult asyncResult = - delegateController.BeginInvokePowerShell(commandToRun, input, - output, - implementationContext.PSActivityEnvironment, - ActivityHostManagerCallback, - args); - psActivityContext.AsyncResults.Enqueue(asyncResult); - } - } - } - break; - - case CommandRunRemotely: - { - ArgsTableForRunspaces.TryAdd(commandToRun.Runspace.InstanceId, args); - InitializeRunspaceAndExecuteCommandWorker(args); - } - break; - case CommandRunInProc: - { - commandToRun.Runspace.ThreadOptions = PSThreadOptions.UseCurrentThread; - ThreadPool.QueueUserWorkItem(InitializeRunspaceAndExecuteCommandWorker, args); - } - break; - case CleanupActivity: - { - ExecuteCleanupActivity(args); - } - break; - } - actionTracer.WriteMessage("END BeginExecuteOneCommand"); - } - } - - /// - /// Calls the DoCleanup method of the cleanup activity - /// - /// RunCommandsArguments - /// - /// THREADING CONTRACT: - /// This function runs on the workflow thread when Execute() - /// is called - /// Therefore this function is designed to be lightweight in - /// all cases - /// When any changes are made to this function the contract needs to - /// be honored - private static void ExecuteCleanupActivity(RunCommandsArguments args) - { - PSCleanupActivity cleanupActivity = args.ActivityObject as PSCleanupActivity; - if (cleanupActivity == null) - { - throw new ArgumentNullException("args"); - } - cleanupActivity.DoCleanup(args, CleanupActivityCallback); - } - - /// - /// Callback when all connections to the specified computer - /// are closed - /// - /// RunCommandsArguments that the activity - /// passed int - /// - /// THREADING CONTRACT: - /// The callback will happen on a WinRM thread. Hence the - /// function needs to be lightweight to release the thread - /// back to WinRM - /// - private static void CleanupActivityCallback(object state) - { - RunCommandsArguments args = state as RunCommandsArguments; - Dbg.Assert(args != null, "Clean up activity should pass back RunCommandsArguments"); - DecrementRunningCountAndCheckForEnd(args.PSActivityContext); - } - - private static void ExecuteOneRunspaceFreeCommandWorker(object state) - { - Dbg.Assert(state != null, "State needs to be passed to ExecuteOneWmiCommandWorker"); - RunCommandsArguments args = state as RunCommandsArguments; - Dbg.Assert(args != null, "RunCommandsArguments not passed correctly to ExecuteOneWmiCommandWorker"); - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - PSDataCollection input = args.Input; - PSDataCollection output = args.Output; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - bool attemptRetry = false; - try - { - actionTracer.WriteMessage("Running WMI/CIM generic activity on ThreadPool thread"); - RunDirectExecutionActivity(implementationContext.PowerShellInstance, input, output, psActivityContext, implementationContext); - } - catch (Exception e) - { - PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource(); - tracer.TraceException(e); - - attemptRetry = HandleRunOneCommandException(args, e); - if (attemptRetry) - BeginActionRetry(args); - } - finally - { - implementationContext.CleanUp(); - - RunOneCommandFinally(args, attemptRetry); - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity: Finished running command.")); - // decrement count of running commands - DecrementRunningCountAndCheckForEnd(psActivityContext); - } - } - - } - - private static void BeginActionRetry(RunCommandsArguments args) - { - Dbg.Assert(args != null, "Arguments not passed correctly to BeginActionRetry"); - PSActivityContext psActivityContext = args.PSActivityContext; - - Interlocked.Increment(ref psActivityContext.CommandsRunningCount); - BeginRunOneCommand(args); - } - - private static bool HandleRunOneCommandException(RunCommandsArguments args, Exception e) - { - bool attemptRetry = false; - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - ActivityParameters activityParameters = args.ActivityParameters; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Exception handling for command {0}.", commandToRun)); - actionTracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Got exception running command: {0}.", e.Message)); - -#if(DEBUG) - // Synchronization code to help tests verify that canceling during error handling doesn't - // cause unhandled exceptions. - const string timingTest = "Test.Cancellation.During.Error.Handling"; - if (Environment.GetEnvironmentVariable(timingTest) != null) - { - while (!String.Equals(Environment.GetEnvironmentVariable(timingTest), - "TestReady", StringComparison.OrdinalIgnoreCase)) - { - Thread.Sleep(50); - } - Environment.SetEnvironmentVariable(timingTest, null); - } - Thread.Sleep(100); -#endif - - // If we've used more attempts than retries, - // this is a fatal error. We initialize this to MaxValue in case the activity was canceled, - // in which case we want no more retries. - int attempts = Int32.MaxValue; - - // No need add the exceptions in the list if the activity is already canceled. - if (!psActivityContext.IsCanceled) - { - // if there is a connection failure then Connection manager - // callback will handle the same. However if we got a connection - // and it broke while we are preparing we need to attempt a retry - if (psActivityContext.runningCommands.ContainsKey(commandToRun)) - attempts = psActivityContext.runningCommands[commandToRun].ActionAttempts; - - attemptRetry = HandleFailure(attempts, activityParameters.ActionRetryCount, - activityParameters.ActionRetryInterval, - implementationContext, "ActivityActionFailed", e, - psActivityContext); - } - } - - return attemptRetry; - } - - private static void RunOneCommandFinally(RunCommandsArguments args, bool attemptRetry) - { - if (attemptRetry) return; - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - PSWorkflowHost workflowHost = args.WorkflowHost; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - - // Once we're done, remove this command from the list - // of running commands. - lock (psActivityContext.runningCommands) - { - psActivityContext.runningCommands.Remove(commandToRun); - } - - // Discard the runspace - don't need to do this for activities that don't use a runspace - if (!psActivityContext.IsCanceled && args.CommandExecutionType != RunInProcNoRunspace) - { - CloseRunspaceAndDisposeCommand(commandToRun, workflowHost, psActivityContext, args.CommandExecutionType); - } - } - - /// - /// Callback from connection manager - /// - /// - /// - /// THREADING CONTRACT: - /// The callback happens either in a WinRM thread or in the - /// connection manager servicing thread. Therefore any - /// operations that this thread initiates is supposed to - /// be very small. Make sure that this contract is maintained - /// when any changes are made to the function - private static void ConnectionManagerCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by connection manager"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - PSWorkflowHost workflowHost = args.WorkflowHost; - PSActivityContext psActivityContext = args.PSActivityContext; - WSManConnectionInfo connectionInfo = commandToRun.Runspace.ConnectionInfo as WSManConnectionInfo; - Dbg.Assert(connectionInfo != null, "ConnectionInfo cannot be null"); - string[] psComputerName = implementationContext.PSComputerName; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for GetRunspace for computer ", - commandToRun.Runspace.ConnectionInfo.ComputerName); - Runspace runspace = null; - try - { - runspace = workflowHost.RemoteRunspaceProvider.EndGetRunspace(asyncResult); - } - catch (Exception exception) - { - // there is an error in connecting to the specified computer - // handle the same - tracer.WriteMessage("Error in connecting to remote computer ", - commandToRun.Runspace.ConnectionInfo.ComputerName); - // If this was a multi-machine activity, this is an error, not an exception.) - if ((psComputerName != null) && (psComputerName.Length > 1)) - { - WriteError(exception, "ConnectionAttemptFailed", - ErrorCategory.InvalidResult, - psComputerName, psActivityContext); - } - else - { - var failureErrorRecord = new ErrorRecord(exception, "ConnectionAttemptFailed", ErrorCategory.OpenError, - psComputerName); - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(new RuntimeException(exception.Message, exception, - failureErrorRecord)); - } - } - - // when runspace connection cannot be obtained we will not retry - RunOneCommandFinally(args, false); - // when runspace cannot be obtained we cannot execute the command - // simply decrement count and return - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - - // runspace should be in an opened state when connection manager - // assigns the same - tracer.WriteMessage("Runspace successfully obtained with guid ", runspace.InstanceId.ToString()); - - // Before the connection was assigned, the activity could have been cancelled, therefore - // we need to check and release before return. - if (psActivityContext.IsCanceled) - { - CloseRunspace(runspace, CommandRunRemotely, workflowHost, psActivityContext); - if (CheckForCancel(psActivityContext)) return; - } - - // In case of auto reconnect after crash/shutdown, the disconnected remote runspace is already assigned to the disconnected powershell instance - // Assign the runspace only if instance ids are different - if (!commandToRun.Runspace.InstanceId.Equals(runspace.InstanceId)) - commandToRun.Runspace = runspace; - - // the handler is saved in the context so that the registration - // can be unregistered later - psActivityContext.HandleRunspaceStateChanged = HandleRunspaceStateChanged; - commandToRun.Runspace.StateChanged += psActivityContext.HandleRunspaceStateChanged; - - // Invocation state can be in running or final state in reconnect scenario - // Command should be invoked only when invocation state is not started, - // during the normal scenario, invocation state is always NotStarted as runspace is just assigned - if (commandToRun.InvocationStateInfo.State == PSInvocationState.NotStarted) - { - BeginExecuteOneCommand(args); - } - - tracer.WriteMessage("Returning from callback for GetRunspace for computer ", - commandToRun.Runspace.ConnectionInfo.ComputerName); - } - } - - private static void HandleRunspaceStateChanged(object sender, RunspaceStateEventArgs eventArgs) - { - if (!( (eventArgs.RunspaceStateInfo.State == RunspaceState.Opened) || - (eventArgs.RunspaceStateInfo.State == RunspaceState.Disconnected))) - return; - - Runspace borrowedRunspace = sender as Runspace; - RunCommandsArguments args; - - Dbg.Assert(borrowedRunspace != null, "Sender needs to be Runspace object"); - ArgsTableForRunspaces.TryGetValue(borrowedRunspace.InstanceId, out args); - - if (args == null) - return; - - System.Management.Automation.PowerShell commandToRun = args.ImplementationContext.PowerShellInstance; - PSWorkflowHost workflowHost = args.PSActivityContext.WorkflowHost; - - if (eventArgs.RunspaceStateInfo.State == RunspaceState.Opened && - commandToRun.InvocationStateInfo.State == PSInvocationState.Disconnected) - { - commandToRun.ConnectAsync(); - return; - } - - if (eventArgs.RunspaceStateInfo.State == RunspaceState.Disconnected) - { - // the connection manager can disconnect a runspace on purpose, if that - // is not the case then it is a case of network disconnect. In this case - // we force a failure - if (!workflowHost.RemoteRunspaceProvider.IsDisconnectedByRunspaceProvider(borrowedRunspace)) - { - ArgsTableForRunspaces.TryRemove(borrowedRunspace.InstanceId, out args); - RuntimeException exception = - new RuntimeException( - String.Format(CultureInfo.CurrentCulture, - Resources.ActivityFailedDueToRunspaceDisconnect, - borrowedRunspace.ConnectionInfo.ComputerName), - eventArgs.RunspaceStateInfo.Reason); - RunspaceDisconnectedCallback(args, exception); - } - } - } - - /// - /// Sets the $pwd variable in the current runspace - /// - /// - /// - private static void SetCurrentDirectory(PSActivityContext psActivityContext, Runspace runspace) - { - if (psActivityContext.ParameterDefaults != null) - { - if (psActivityContext.ParameterDefaults.ContainsKey(Constants.PSCurrentDirectory)) - { - string path = psActivityContext.ParameterDefaults[Constants.PSCurrentDirectory] as string; - - if (path != null) - { - runspace.SessionStateProxy.Path.SetLocation(path); - } - } - } - } - - - /// - /// Callback from local runspace provider - /// - /// This callback happens in the workflow - /// thread or a threadpool callback servicing thread. - /// However there is only one thread for servicing all callbacks and - /// so all operations have to be small - private static void LocalRunspaceProviderCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by LocalRunspaceProvider"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - PSWorkflowHost workflowHost = args.WorkflowHost; - PSActivityContext psActivityContext = args.PSActivityContext; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for LocalRunspaceProvider"); - Runspace runspace = null; - try - { - runspace = workflowHost.LocalRunspaceProvider.EndGetRunspace(asyncResult); - - if (runspace.ConnectionInfo == null) - { - if (psActivityContext.UserVariables.Count != 0) - { - foreach (KeyValuePair entry in psActivityContext.UserVariables) - { - runspace.SessionStateProxy.SetVariable(entry.Key, entry.Value); - } - } - - SetCurrentDirectory(psActivityContext, runspace); - } - } - catch (Exception exception) - { - - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(exception); - } - - // when runspace connection cannot be obtained we will not retry - RunOneCommandFinally(args, false); - // when runspace cannot be obtained we cannot execute the command - // simply decrement count and return - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - - // runspace should be in an opened state when connection manager - // assigns the same - tracer.WriteMessage("Local Runspace successfully obtained with guid ", runspace.InstanceId.ToString()); - - // Before the runspace is assigned, the activity could have been cancelled, therefore - // we need to check and release before return. - if (psActivityContext.IsCanceled) - { - CloseRunspace(runspace, CommandRunInProc, workflowHost, psActivityContext); - if (CheckForCancel(psActivityContext)) return; - } - - commandToRun.Runspace = runspace; - - // only after the runspace is set to commandToRun is the - // activity completely created. Raise the ActivityCreated - // event now - OnActivityCreated(args.ActivityObject, new ActivityCreatedEventArgs(commandToRun)); - - if (args.CommandExecutionType == CimCommandRunInProc) - { - CimActivityImplementationContext cimImplContext = - implementationContext as CimActivityImplementationContext; - Dbg.Assert(cimImplContext != null, - "If CommandExecutionType is CimCommand, then implementationContext must be of type CimActivityImplementationContext"); - - // Configure the runspace by executing the module definition scriptblock... - Dbg.Assert(cimImplContext.ModuleScriptBlock != null, "A Generated CIM activity should never have a null module scriptblock"); - runspace.SessionStateProxy.InvokeCommand.InvokeScript(false, cimImplContext.ModuleScriptBlock, null); - - // Get the CIM session if needed... - if (cimImplContext.Session == null && !string.IsNullOrEmpty(cimImplContext.ComputerName) && - !string.Equals(cimImplContext.ComputerName, "localhost", - StringComparison.OrdinalIgnoreCase)) - { - bool useSsl = false; - if (cimImplContext.PSUseSsl.HasValue) - { - useSsl = cimImplContext.PSUseSsl.Value; - } - - uint port = 0; - if (cimImplContext.PSPort.HasValue) - { - port = cimImplContext.PSPort.Value; - } - - - AuthenticationMechanism authenticationMechanism = AuthenticationMechanism.Default; - if (cimImplContext.PSAuthentication.HasValue) - { - authenticationMechanism = cimImplContext.PSAuthentication.Value; - } - - cimImplContext.Session = - CimConnectionManager.GetGlobalCimConnectionManager().GetSession(cimImplContext.ComputerName, - cimImplContext.PSCredential, - cimImplContext.PSCertificateThumbprint, - authenticationMechanism, - cimImplContext.SessionOptions, - useSsl, - port, - cimImplContext.PSSessionOption); - - if (cimImplContext.Session == null) - { - throw new InvalidOperationException(); - } - commandToRun.AddParameter("CimSession", cimImplContext.Session); - } - } - - BeginExecuteOneCommand(args); - - tracer.WriteMessage("Returning from callback for GetRunspace for LocalRunspaceProvider"); - } - } - - private static void ActivityHostManagerCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by activity host manager"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - - PSWorkflowHost workflowHost = args.WorkflowHost; - PSActivityContext psActivityContext = args.PSActivityContext; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for Executing command out of proc"); - bool attemptRetry = false; - try - { - ((PSOutOfProcessActivityController)workflowHost.PSActivityHostController).EndInvokePowerShell(asyncResult); - } - catch (Exception e) - { - attemptRetry = HandleRunOneCommandException(args, e); - if (attemptRetry) - BeginActionRetry(args); - } - finally - { - RemoveHandlersFromStreams(args.ImplementationContext.PowerShellInstance, args); - RunOneCommandFinally(args, attemptRetry); - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity: Finished running command.")); - DecrementRunningCountAndCheckForEnd(psActivityContext); - } - } - } - - private static void DecrementRunningCountAndCheckForEnd(PSActivityContext psActivityContext) - { - Interlocked.Decrement(ref psActivityContext.CommandsRunningCount); - - if (psActivityContext.CommandsRunningCount != 0) return; - - RaiseTerminalCallback(psActivityContext); - } - - private static void RaiseTerminalCallback(PSActivityContext psActivityContext) - { - lock (psActivityContext.SyncRoot) - { - if (psActivityContext.AllCommandsStarted || psActivityContext.commandQueue.Count == 0) - { - // This signals all commands are done - // time to start finish routines - Dbg.Assert(psActivityContext.Callback != null, "A callback should have been assigned in the context"); - ThreadPool.QueueUserWorkItem(psActivityContext.Callback, psActivityContext.AsyncState); - } - } - } - - private static bool CheckForCancel(PSActivityContext psActivityContext) - { - bool canceled = psActivityContext.IsCanceled; - - if (canceled) - RaiseTerminalCallback(psActivityContext); - - return canceled; - } - - private static void RunDirectExecutionActivity(System.Management.Automation.PowerShell commandToRun, PSDataCollection input, - PSDataCollection output, PSActivityContext psActivityContext, ActivityImplementationContext implementationContext) - { - Command command = commandToRun.Commands.Commands[0]; - var cmdName = command.CommandText; - - Cmdlet cmdlet = null; - bool takesInput = false; - if (string.Equals(cmdName, "Get-WMIObject", StringComparison.OrdinalIgnoreCase)) - { - cmdlet = new Microsoft.PowerShell.Commands.GetWmiObjectCommand(); - } - else if (string.Equals(cmdName, "Invoke-WMIMethod", StringComparison.OrdinalIgnoreCase)) - { - cmdlet = new Microsoft.PowerShell.Commands.InvokeWmiMethod(); - takesInput = true; - } - - if (CheckForCancel(psActivityContext)) return; - - // Set up the command runtime instance... - DirectExecutionActivitiesCommandRuntime cmdRuntime = new DirectExecutionActivitiesCommandRuntime(output != null ? output : new PSDataCollection(), - implementationContext, cmdlet != null ? cmdlet.GetType() : psActivityContext.TypeImplementingCmdlet); - cmdRuntime.Error = commandToRun.Streams.Error; - cmdRuntime.Warning = commandToRun.Streams.Warning; - cmdRuntime.Progress = commandToRun.Streams.Progress; - cmdRuntime.Verbose = commandToRun.Streams.Verbose; - cmdRuntime.Debug = commandToRun.Streams.Debug; - cmdRuntime.Information = commandToRun.Streams.Information; - - // If the cmdlet takes input and there is or may be some input, then - // iterate processing the input - - if (cmdlet != null) - { - cmdlet.CommandRuntime = cmdRuntime; - - // Copy the parameters from the PowerShell object to the cmdlet object - PSObject wrappedCmdlet = PSObject.AsPSObject(cmdlet); - InitializeCmdletInstanceParameters(command, wrappedCmdlet, false, psActivityContext, null, implementationContext); - - if (takesInput && input != null && (input.Count > 0 || input.IsOpen)) - { - Microsoft.PowerShell.Commands.InvokeWmiMethod iwm = cmdlet as Microsoft.PowerShell.Commands.InvokeWmiMethod; - foreach (PSObject inputObject in input) - { - try - { - var managementObject = LanguagePrimitives.ConvertTo(inputObject); - - iwm.InputObject = managementObject; - iwm.Invoke().GetEnumerator().MoveNext(); - } - catch (PSInvalidCastException psice) - { - if (psice.ErrorRecord != null) - { - cmdRuntime.Error.Add(psice.ErrorRecord); - } - } - - if (CheckForCancel(psActivityContext)) return; - } - } - else - { - cmdlet.Invoke().GetEnumerator().MoveNext(); - } - } - else - { - // See if any session options were passed.. - CimActivityImplementationContext cimActivityImplementationContext = implementationContext as CimActivityImplementationContext; - CimSessionOptions cimSessionOptionsToUse = cimActivityImplementationContext != null ? cimActivityImplementationContext.SessionOptions : null; - - if (psActivityContext.TypeImplementingCmdlet == null) - { - throw new InvalidOperationException(cmdName); - } - - - if (input != null && (input.Count > 0 || input.IsOpen)) - { - // Only deals with InputObject property for input - if (psActivityContext.TypeImplementingCmdlet.GetProperty("InputObject") == null) - { - // Throw if the cmdlet does not implement a InputObject property to bind to.... - throw new NotImplementedException(String.Format(CultureInfo.CurrentCulture, - Resources.CmdletDoesNotImplementInputObjectProperty, - cmdName)); - } - - foreach (PSObject inputObject in input) - { - try - { - using (var cimCmdlet = (Microsoft.Management.Infrastructure.CimCmdlets.CimBaseCommand)Activator.CreateInstance(psActivityContext.TypeImplementingCmdlet)) - { - cimCmdlet.CommandRuntime = cmdRuntime; - var cimInstance = LanguagePrimitives.ConvertTo(inputObject); - PSObject wrapper = PSObject.AsPSObject(cimCmdlet); - InitializeCmdletInstanceParameters(command, wrapper, true, psActivityContext, cimSessionOptionsToUse, implementationContext); - var prop = wrapper.Properties["InputObject"]; - prop.Value = cimInstance; - cimCmdlet.Invoke().GetEnumerator().MoveNext(); - } - } - catch (PSInvalidCastException psice) - { - if (psice.ErrorRecord != null) - { - cmdRuntime.Error.Add(psice.ErrorRecord); - } - } - - if (CheckForCancel(psActivityContext)) return; - } - } - else - { - using (var cimCmdlet = (Microsoft.Management.Infrastructure.CimCmdlets.CimBaseCommand)Activator.CreateInstance(psActivityContext.TypeImplementingCmdlet)) - { - cimCmdlet.CommandRuntime = cmdRuntime; - PSObject wrapper = PSObject.AsPSObject(cimCmdlet); - InitializeCmdletInstanceParameters(command, wrapper, true, psActivityContext, cimSessionOptionsToUse, implementationContext); - cimCmdlet.Invoke().GetEnumerator().MoveNext(); - } - } - } - } - - private static void InitializeCmdletInstanceParameters(Command command, PSObject wrappedCmdlet, bool isGenericCim, - PSActivityContext psActivityContext, CimSessionOptions cimSessionOptions, ActivityImplementationContext implementationContext) - { - - bool sessionSet = false; - - foreach (CommandParameter p in command.Parameters) - { - // Note - skipping null common parameters avoids a null pointer exception. - // Also need to replicate parameter set validation for the WMI activities at this point - // since we aren't going through the parameter binder - if (Cmdlet.CommonParameters.Contains(p.Name)) - { - continue; - } - - // If a session was explicitly passed, use it instead of an ambient session for the current computer. - if (p.Name.Equals("CimSession")) - { - sessionSet = true; - } - - if (wrappedCmdlet.Properties[p.Name] != null) - { - wrappedCmdlet.Properties[p.Name].Value = p.Value; - } - else - { - wrappedCmdlet.Properties.Add(new PSNoteProperty(p.Name, p.Value)); - } - } - - string[] computerNameArray = null; - CimActivityImplementationContext cimActivityImplementationContext = - implementationContext as CimActivityImplementationContext; - - // Set the target computer for this command... - if (cimActivityImplementationContext != null && !String.IsNullOrEmpty(cimActivityImplementationContext.ComputerName)) - { - computerNameArray = new string[] { cimActivityImplementationContext.ComputerName }; - } - else if(psActivityContext.ParameterDefaults.ContainsKey("PSComputerName")) - { - computerNameArray = psActivityContext.ParameterDefaults["PSComputerName"] as string[]; - } - - if (computerNameArray != null && computerNameArray.Length > 0) - { - // Not all of the generic CIM cmdlets take a session (e.g. New-CimSession) - if (isGenericCim && wrappedCmdlet.Properties["CimSession"] != null) - { - if (!sessionSet) - { - if (cimActivityImplementationContext == null) - throw new ArgumentException(Resources.InvalidImplementationContext); - - bool useSsl = false; - if (cimActivityImplementationContext.PSUseSsl.HasValue) - { - useSsl = cimActivityImplementationContext.PSUseSsl.Value; - } - - uint port = 0; - if (cimActivityImplementationContext.PSPort.HasValue) - { - port = cimActivityImplementationContext.PSPort.Value; - } - - AuthenticationMechanism authenticationMechanism = AuthenticationMechanism.Default; - if (cimActivityImplementationContext.PSAuthentication.HasValue) - { - authenticationMechanism = cimActivityImplementationContext.PSAuthentication.Value; - } - - // Convert the computer names to session objects. - List cimSessions = computerNameArray - .ToList() - .ConvertAll( - (string computer) => CimConnectionManager.GetGlobalCimConnectionManager().GetSession(computer, - cimActivityImplementationContext.PSCredential, - cimActivityImplementationContext.PSCertificateThumbprint, - authenticationMechanism, - cimSessionOptions, - useSsl, - port, - cimActivityImplementationContext.PSSessionOption)); - wrappedCmdlet.Properties["CimSession"].Value = cimSessions.ToArray(); - - if (computerNameArray.Length > 1) - { - Dbg.Assert(false, "Something in this fix is not right, have a look"); - } - cimActivityImplementationContext.Session = cimSessions[0]; - } - } - else if (wrappedCmdlet.Properties["ComputerName"] == null) - { - // If the cmdlet takes ComputerName and it wasn't explicitly set already, - // then set it to the ambient PSComputerName we got from the context... - wrappedCmdlet.Properties.Add(new PSNoteProperty("ComputerName", computerNameArray)); - } - } - } - - // Script to initialize the runspace variables... - private const string RunspaceInitScript = @" - Get-Variable -Exclude input | Remove-Variable -ErrorAction Ignore; $input | Foreach-Object {$nvp=$_}; foreach($k in $nvp.keys){set-variable -name $k -value $nvp[$k]} - "; - internal static readonly InitialSessionState Iss = InitialSessionState.CreateDefault(); - - private static void SetVariablesInRunspaceUsingProxy(PSActivityEnvironment activityEnvironment, Runspace runspace) - { - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - actionTracer.WriteMessage("BEGIN SetVariablesInRunspaceUsingProxy"); - Dictionary nameValuePairs = GetVariablesToSetInRunspace(activityEnvironment); - // Copy in the workflow variables - foreach (string name in nameValuePairs.Keys) - { - object value = nameValuePairs[name]; - if (value != null) - { - try - { - var psvar = runspace.SessionStateProxy.PSVariable.Get(name); - if (psvar == null || (psvar.Options & ScopedItemOptions.ReadOnly) == 0) - { - // don't try to overwrite read-only variables values - runspace.SessionStateProxy.PSVariable.Set(name, value); - } - } - catch (PSNotSupportedException) - { - actionTracer.WriteMessage("SetVariablesInRunspaceUsingProxy: Copying the workflow variables to a RemoteSessionStateProxy is not supported."); - return; - } - } - } - actionTracer.WriteMessage("END SetVariablesInRunspaceUsingProxy"); - } - } - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This function runs in either a WinRM thread or in the - /// connection manager servicing thread. Therefore any - /// operations that this thread initiates is supposed to - /// be very small. Make sure that this contract is maintained - /// when any changes are made to the function - /// - private static void BeginSetVariablesInRemoteRunspace(RunCommandsArguments args) - { - Runspace runspace = args.ImplementationContext.PowerShellInstance.Runspace; - PSActivityEnvironment activityEnvironment = args.ImplementationContext.PSActivityEnvironment; - - Dbg.Assert(runspace.ConnectionInfo != null, - "BeginSetVariablesInRemoteRunspace can only be called for remote runspaces"); - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - actionTracer.WriteMessage("BEGIN BeginSetVariablesInRemoteRunspace"); - - System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create(); - ps.Runspace = runspace; - ps.AddScript(RunspaceInitScript); - - Dictionary nameValuePairs = GetVariablesToSetInRunspace(activityEnvironment); - - PSDataCollection vars = new PSDataCollection {nameValuePairs}; - vars.Complete(); - args.HelperCommand = ps; - args.HelperCommandInput = vars; - - BeginInvokeOnPowershellCommand(ps, vars, null, SetVariablesCallback, args); - - actionTracer.WriteMessage("END BeginSetVariablesInRemoteRunspace"); - } - } - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This function either runs on a thread pool thread, or in the - /// remote case runs on a WinRM/CM servicing thread. Therefore operations - /// need to be light so that the thread can be released back quickly - /// When changes are made this assumption need to be validated - private static void BeginRunspaceInitializeSetup(RunCommandsArguments args) - { - PSActivityEnvironment activityEnvironment= args.ImplementationContext.PSActivityEnvironment; - Runspace runspace = args.ImplementationContext.PowerShellInstance.Runspace; - string[] requiredModules = args.ActivityParameters.PSRequiredModules; - PSActivityContext psActivityContext = args.PSActivityContext; - - using (PowerShellTraceSource actionTracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - actionTracer.WriteMessage("BEGIN BeginRunspaceInitializeSetup"); - if (args.CommandExecutionType != CimCommandRunInProc) - { - if (runspace.ConnectionInfo != null) - { - // for a remote runspace do an async invocation - BeginSetVariablesInRemoteRunspace(args); - return; - } - - // for a local runspace set the variables using proxy - // on the same thread - try - { - SetVariablesInRunspaceUsingProxy(activityEnvironment, runspace); - } - catch (Exception e) - { - bool attemptRetry = HandleRunOneCommandException(args, e); - - if (attemptRetry) - { - actionTracer.WriteMessage("Setting variables for command failed, attempting retry"); - // before attempting a retry we will have to ditch this runspace - // and use a new one - CloseRunspace(args.ImplementationContext.PowerShellInstance.Runspace, - args.CommandExecutionType, - args.WorkflowHost, psActivityContext); - BeginActionRetry(args); - } - else - { - // if we are not attempting a retry we just need to - // return - actionTracer.WriteMessage("Setting variables for command failed, returning"); - RunOneCommandFinally(args, false); - } - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - } - - if (requiredModules.Length > 0) - { - BeginImportRequiredModules(args); - } - else - { - // else we are good to call command invocation this point - // since this function is guaranteed to run on a threadpool - // thread BeginPowerShellInvocation can be directly called here - BeginPowerShellInvocation(args); - } - - actionTracer.WriteMessage("END BeginRunspaceInitializeSetup"); - } - } - - private static void BeginImportRequiredModules(RunCommandsArguments args) - { - Runspace runspace = args.ImplementationContext.PowerShellInstance.Runspace; - PSActivityEnvironment activityEnvironment = args.ImplementationContext.PSActivityEnvironment; - System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create(); - if (activityEnvironment != null) - { - Dbg.Assert(activityEnvironment.Modules.Count > 0, - "When PSActivityEnvironment is specified and modules are imported, PSActivityEnvironment.Modules need to be populated"); - // Setting erroraction to stop for import-module since they are required modules. If not present, stop the execution - ps.AddCommand("Import-Module").AddParameter("Name", activityEnvironment.Modules).AddParameter( - "ErrorAction", ActionPreference.Stop); - } - else - { - // Setting erroraction to stop for import-module since they are required modules. If not present, stop the execution - ps.AddCommand("Import-Module").AddParameter("Name", args.ActivityParameters.PSRequiredModules).AddParameter( - "ErrorAction", ActionPreference.Stop); - } - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - // we need to import required modules only if any are specified - tracer.WriteMessage("Importing modules in runspace ", runspace.InstanceId); - - ps.Runspace = runspace; - args.HelperCommand = ps; - - BeginInvokeOnPowershellCommand(ps, null, null, ImportRequiredModulesCallback, args); - } - } - - private static void BeginInvokeOnPowershellCommand(System.Management.Automation.PowerShell ps, PSDataCollection varsInput, PSInvocationSettings settings, AsyncCallback callback, RunCommandsArguments args) - { - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - try - { - ps.BeginInvoke(varsInput, settings, callback, args); - } - catch (Exception e) - { - bool attemptRetry = ProcessException(args, e); - - if (attemptRetry) - { - BeginActionRetry(args); - } - else - { - ReleaseResourcesAndCheckForEnd(ps, args, false, false); - } - - tracer.TraceException(e); - } - } - } - - private static void ReleaseResourcesAndCheckForEnd(System.Management.Automation.PowerShell ps, RunCommandsArguments args, bool removeHandlersFromStreams, bool attemptRetry) - { - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - PSActivityContext psActivityContext = args.PSActivityContext; - - if (removeHandlersFromStreams) - { - RemoveHandlersFromStreams(ps, args); - } - - RunOneCommandFinally(args, attemptRetry); - - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity: Finished running command.")); - - var remotingActivity = args.ActivityObject as PSRemotingActivity; - if ((remotingActivity != null) && - (args.PSActivityContext.HostExtension != null) && - (args.PSActivityContext.HostExtension.RemoteActivityState != null) && - (args.ImplementationContext != null)) - { - // remote activity task execution has finished - // change the task's state to Completed in RemoteActivityState - args.PSActivityContext.HostExtension.RemoteActivityState.SetRemoteActivityRunspaceEntry( - remotingActivity.Id, - args.ImplementationContext.Id, - "completed", null); - } - - DecrementRunningCountAndCheckForEnd(psActivityContext); - } - } - - private static bool ProcessException(RunCommandsArguments args, Exception e) - { - bool attemptRetry = false; - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - - if ((args.ActivityParameters.ConnectionRetryCount.HasValue || - args.ActivityParameters.ConnectionRetryInterval.HasValue) && e.InnerException != null && - e.InnerException is IContainsErrorRecord) - { - IContainsErrorRecord er = e as IContainsErrorRecord; - - // CIM specific case - if (er.ErrorRecord.FullyQualifiedErrorId.StartsWith("CimJob_BrokenCimSession", StringComparison.OrdinalIgnoreCase)) - { - int attempts = Int32.MaxValue; - - if (!psActivityContext.IsCanceled) - { - if (psActivityContext.runningCommands.ContainsKey(commandToRun)) - attempts = psActivityContext.runningCommands[commandToRun].ActionAttempts; - - attemptRetry = HandleFailure(attempts, args.ActivityParameters.ConnectionRetryCount, - args.ActivityParameters.ConnectionRetryInterval, implementationContext, "ActivityActionFailed", - null, psActivityContext); - } - - if(!attemptRetry) - { - ErrorRecord errorRecord = er.ErrorRecord; - String computerName; - Guid jobInstanceId; - if (GetComputerNameAndJobIdForCommand(commandToRun.InstanceId, out computerName, out jobInstanceId)) - { - AddIdentifierInfoToErrorRecord(errorRecord, computerName, jobInstanceId); - } - - // If this was a multi-machine activity, this is an error, not an exception. - if ((implementationContext.PSComputerName != null) && (implementationContext.PSComputerName.Length > 1)) - { - WriteError(e, "ActivityActionFailed", ErrorCategory.InvalidResult, - implementationContext.PowerShellInstance.Runspace.ConnectionInfo, psActivityContext); - } - else - { - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(e); - } - } - } - - return attemptRetry; - } - } - - IContainsErrorRecord containsErrorRecord = e as IContainsErrorRecord; - if (containsErrorRecord != null) - { - ErrorRecord errorRecord = containsErrorRecord.ErrorRecord; - String computerName; - Guid jobInstanceId; - if (GetComputerNameAndJobIdForCommand(commandToRun.InstanceId, out computerName, out jobInstanceId)) - { - AddIdentifierInfoToErrorRecord(errorRecord, computerName, jobInstanceId); - } - } - attemptRetry = HandleRunOneCommandException(args, e); - - return attemptRetry; - } - - private static Dictionary GetVariablesToSetInRunspace(PSActivityEnvironment activityEnvironment) - { - // first set the predefined variables to reset the runspace before - // setting the activity specified ones - Dictionary nameValuePairs = Iss.Variables.ToDictionary(entry => entry.Name, entry => entry.Value); - - if (activityEnvironment != null && activityEnvironment.Variables != null) - { - foreach (string name in activityEnvironment.Variables.Keys) - { - object value = activityEnvironment.Variables[name]; - if (value == null) continue; - if (nameValuePairs.ContainsKey(name)) - { - nameValuePairs[name] = value; - } - else - { - nameValuePairs.Add(name, value); - } - } - } - - // workaround: PSPR does not support rehydration of System.Text.ASCIIEncoding - // therefore we need to remove this otherwise FS RI is blocked - per their dev mgr - // once the DCR is implemented on Engines side, we should remove - if (nameValuePairs.ContainsKey("OutputEncoding")) - { - nameValuePairs.Remove("OutputEncoding"); - } - - return nameValuePairs; - } - - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This callback runs in a WinRM thread - in the normal - /// course of operation i.e unless PowerShell.EndInvoke() throws - /// an exception, the operations performed on this thread - /// should be minimal - private static void SetVariablesCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState was not set correctly by SetVariablesInActivityHost() method"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - System.Management.Automation.PowerShell powerShell = args.HelperCommand; - Dbg.Assert(powerShell != null, "Caller did not pass PowerShell instance correctly"); - - PSActivityContext psActivityContext = args.PSActivityContext; - PSActivityEnvironment activityEnvironment = args.ImplementationContext.PSActivityEnvironment; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for setting variables in remote runspace"); - try - { - powerShell.EndInvoke(asyncResult); - } - catch (Exception) - { - // Exception setting variables, try using proxy - tracer.WriteMessage("Setting variables in remote runspace failed using script, trying with proxy"); - - try - { - SetVariablesInRunspaceUsingProxy(activityEnvironment, powerShell.Runspace); - } - catch (Exception ex) - { - bool attemptRetry = HandleRunOneCommandException(args, ex); - if (attemptRetry) - { - tracer.WriteMessage("Runspace initialization failed, attempting retry"); - // Since the error is in runspace initialization, we need to discard this - // runspace and try with a new one - CloseRunspace(args.ImplementationContext.PowerShellInstance.Runspace, - args.CommandExecutionType, - args.WorkflowHost, psActivityContext); - BeginActionRetry(args); - } - else - { - RunOneCommandFinally(args, false); - } - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - } - finally - { - powerShell.Dispose(); - args.HelperCommand = null; - args.HelperCommandInput.Dispose(); - args.HelperCommandInput = null; - } - - if (CheckForCancel(psActivityContext)) return; - - if ((activityEnvironment != null && activityEnvironment.Modules != null && activityEnvironment.Modules.Count > 0) || - (args.ActivityParameters != null && args.ActivityParameters.PSRequiredModules != null && args.ActivityParameters.PSRequiredModules.Length > 0)) - { - // if there are modules to import, begin async invocation - // for importing modules - BeginImportRequiredModules(args); - } - else - { - // else we are good to call command invocation this point - // since this function is guaranteed to run on a threadpool - // thread BeginPowerShellInvocation can be directly called here - BeginPowerShellInvocation(args); - } - } - } - - private static void ImportRequiredModulesCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by activity host manager"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - System.Management.Automation.PowerShell powerShell = args.HelperCommand; - Dbg.Assert(powerShell != null, "Caller did not pass PowerShell instance correctly"); - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityParameters activityParameters = args.ActivityParameters; - Type activityType = args.ActivityType; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for importing required modules"); - try - { - powerShell.EndInvoke(asyncResult); - } - catch (Exception e) - { - string psRequiredModuleNames = ""; - foreach (string moduleName in activityParameters.PSRequiredModules) - { - psRequiredModuleNames += moduleName + ", "; - } - psRequiredModuleNames = psRequiredModuleNames.TrimEnd(',',' '); - - string msg = string.Format(CultureInfo.InvariantCulture, - Resources.DependModuleImportFailed, - psRequiredModuleNames, - activityType.Name); - Exception ex = new Exception(msg, e); - tracer.TraceException(ex); - - bool attemptRetry = HandleRunOneCommandException(args, ex); - if (attemptRetry) - { - tracer.WriteMessage("Runspace initialization failed, attempting retry"); - // Since the error is in runspace initialization, we need to discard this - // runspace and try with a new one - CloseRunspace(args.ImplementationContext.PowerShellInstance.Runspace, args.CommandExecutionType, - args.WorkflowHost, psActivityContext); - BeginActionRetry(args); - } - else - { - RunOneCommandFinally(args, false); - } - DecrementRunningCountAndCheckForEnd(psActivityContext); - return; - } - finally - { - powerShell.Dispose(); - args.HelperCommand = null; - } - - if (CheckForCancel(psActivityContext)) return; - - // at this point the initialization is done and we need to execute - // the command - BeginPowerShellInvocation(args); - } - } - - private static void InitializeRunspaceAndExecuteCommandWorker(object state) - { - Dbg.Assert(state != null, "State not passed correctly to worker"); - RunCommandsArguments args = state as RunCommandsArguments; - Dbg.Assert(args != null, "RunCommandsArguments not passed correctly"); - - System.Management.Automation.PowerShell commandToRun = args.ImplementationContext.PowerShellInstance; - PSActivityContext psActivityContext = args.PSActivityContext; - - // Invoke the command. If we want to return the output, then call the overload - // that streams output as well. - try - { - if (CheckForCancel(psActivityContext)) return; - - // we need to set the variables in the runspace before - // invoking the command - BeginRunspaceInitializeSetup(args); - - } - catch (PipelineStoppedException) - { - } - } - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This function is designed to be lightweight. It always runs - /// on a callback thread - which is a threadpool thread - private static void BeginPowerShellInvocation(object state) - { - Dbg.Assert(state != null, "State not passed correctly to BeginPowerShellInvocation"); - RunCommandsArguments args = state as RunCommandsArguments; - Dbg.Assert(args != null, "Args not passed correctly to BeginPowerShellInvocation"); - - ActivityImplementationContext implementationContext = args.ImplementationContext; - PSDataCollection output = args.Output; - PSDataCollection input = args.Input; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - PSWorkflowHost workflowHost = args.WorkflowHost; - PSActivityContext psActivityContext = args.PSActivityContext; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - PSDataCollection outputForInvoke = output ?? - new PSDataCollection(); - bool exceptionOccurred = false; - - try - { - if (CheckForCancel(psActivityContext)) return; - - AddHandlersToStreams(commandToRun, args); - - if (CheckForCancel(psActivityContext)) return; - - commandToRun.BeginInvoke(input, outputForInvoke, null, - PowerShellInvocationCallback, - args); - } - catch (Exception e) - { - exceptionOccurred = true; - - bool attemptRetry = ProcessException(args, e); - - if (attemptRetry) - { - BeginActionRetry(args); - } - else - { - ReleaseResourcesAndCheckForEnd(commandToRun, args, true, false); - } - - tracer.TraceException(e); - } - - tracer.WriteMessage("Completed BeginInvoke call on PowerShell"); - - if (exceptionOccurred == false && - args.CommandExecutionType == CommandRunRemotely) - { - if ((args.ImplementationContext != null) && (args.ImplementationContext.EnableRemotingActivityAutoResume)) - { - var remotingActivity = args.ActivityObject as PSRemotingActivity; - if ((remotingActivity != null) && - (args.PSActivityContext.HostExtension != null) && - (args.PSActivityContext.HostExtension.RemoteActivityState != null)) - { - // Command invocation has started, save the tasks's runspace instance id to RemoteActivityState - args.PSActivityContext.HostExtension.RemoteActivityState.SetRemoteActivityRunspaceEntry( - remotingActivity.Id, - args.ImplementationContext.Id, - commandToRun.Runspace.InstanceId, null); - } - } - - // if the runspace was obtained from the connection manager - // then it need to be signaled as ready for disconnect/reconnect - workflowHost.RemoteRunspaceProvider.ReadyForDisconnect(commandToRun.Runspace); - } - } - } - - private static readonly ConcurrentDictionary ArgsTableForRunspaces - = new ConcurrentDictionary(); - - #region Object Decoration - - internal static void AddHandlersToStreams(System.Management.Automation.PowerShell commandToRun, RunCommandsArguments args) - { - if (commandToRun == null || args == null) - return; - - bool hasErrorMerged = args.PSActivityContext.MergeErrorToOutput; - - if (hasErrorMerged) - { - commandToRun.Streams.Error.DataAdded += HandleErrorDataAdded; - } - - if (args.PSActivityContext.Output != null) - args.PSActivityContext.Output.DataAdding += HandleOutputDataAdding; - commandToRun.Streams.Error.DataAdding += HandleErrorDataAdding; - commandToRun.Streams.Progress.DataAdding += HandleProgressDataAdding; - commandToRun.Streams.Verbose.DataAdding += HandleInformationalRecordDataAdding; - commandToRun.Streams.Warning.DataAdding += HandleInformationalRecordDataAdding; - commandToRun.Streams.Debug.DataAdding += HandleInformationalRecordDataAdding; - commandToRun.Streams.Information.DataAdding += HandleInformationDataAdding; - - ArgsTable.TryAdd(commandToRun.InstanceId, args); - } - - private static void HandleInformationalRecordDataAdding(object sender, DataAddingEventArgs e) - { - InformationalRecord informationalRecord = (InformationalRecord) e.ItemAdded; - if (informationalRecord == null) return; - - string computerName; - Guid jobInstanceId; - if (GetComputerNameAndJobIdForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId)) - { - informationalRecord.Message = - AddIdentifierInfoToString(jobInstanceId, computerName, informationalRecord.Message); - } - } - - private static void HandleProgressDataAdding(object sender, DataAddingEventArgs e) - { - ProgressRecord progressRecord = (ProgressRecord)e.ItemAdded; - if (progressRecord == null) return; - - string computerName; - Guid jobInstanceId; - - if (GetComputerNameAndJobIdForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId)) - { - progressRecord.CurrentOperation = AddIdentifierInfoToString(jobInstanceId, computerName, - progressRecord.CurrentOperation); - } - } - - private static void HandleInformationDataAdding(object sender, DataAddingEventArgs e) - { - InformationRecord informationRecord = (InformationRecord)e.ItemAdded; - if (informationRecord == null) return; - - string computerName; - Guid jobInstanceId; - - if (GetComputerNameAndJobIdForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId)) - { - informationRecord.Source = AddIdentifierInfoToString(jobInstanceId, computerName, - informationRecord.Source); - } - } - - internal static void AddIdentifierInfoToOutput(PSObject psObject, Guid jobInstanceId, string computerName) - { - Dbg.Assert(psObject != null, "PSObject not passed correctly"); - - if (psObject.Properties["PSComputerName"] != null) - { - // if it is a NoteProperty then try changing the same - PSNoteProperty noteProperty = psObject.Properties["PSComputerName"] as PSNoteProperty; - if (noteProperty != null) - { - try - { - noteProperty.Value = computerName; - } - catch(SetValueException) - { - // this is a best attempt so fine to - // eat exception if PSComputerName is - // defined in type data - } - } - } - else - psObject.Properties.Add(new PSNoteProperty("PSComputerName", computerName)); - - if (psObject.Properties["PSShowComputerName"] != null) - psObject.Properties.Remove("PSShowComputerName"); - psObject.Properties.Add(new PSNoteProperty("PSShowComputerName", true)); - - if (psObject.Properties["PSSourceJobInstanceId"] != null) - psObject.Properties.Remove("PSSourceJobInstanceId"); - psObject.Properties.Add(new PSNoteProperty("PSSourceJobInstanceId", jobInstanceId)); - } - - private static void HandleOutputDataAdding(object sender, DataAddingEventArgs e) - { - PSObject psObject = (PSObject)e.ItemAdded; - if (psObject == null) return; - - string computerName; - Guid jobInstanceId; - - RunCommandsArguments args; - args = GetArgsForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId); - - if (args == null) - return; - - AddIdentifierInfoToOutput(psObject, jobInstanceId, computerName); - } - - private static void HandleErrorDataAdding(object sender, DataAddingEventArgs e) - { - ErrorRecord errorRecord = (ErrorRecord)e.ItemAdded; - if (errorRecord == null) return; - - string computerName; - Guid jobInstanceId; - RunCommandsArguments args = GetArgsForCommand(e.PowerShellInstanceId, out computerName, out jobInstanceId); - - if (args == null) return; - - AddIdentifierInfoToErrorRecord(errorRecord, computerName, args.PSActivityContext.JobInstanceId); - - bool hasHostExtension = args.PSActivityContext.HostExtension != null; - bool hasErrorMerged = args.PSActivityContext.MergeErrorToOutput; - - if (!hasErrorMerged && !hasHostExtension) return; - - HostSettingCommandMetadata sourceCommandMetadata = hasHostExtension - ? args.PSActivityContext.HostExtension. - HostCommandMetadata - : null; - PSDataCollection outputStream = hasErrorMerged ? args.PSActivityContext.Output : null; - - PowerShellInvocation_ErrorAdding(sender, e, sourceCommandMetadata, outputStream); - } - - /// - /// - /// - /// - /// - /// - /// false if default values were substituted - private static bool GetComputerNameAndJobIdForCommand(Guid powerShellId, out string computerName, out Guid jobInstanceId) - { - RunCommandsArguments args = GetArgsForCommand(powerShellId, out computerName, out jobInstanceId); - return args != null; - } - - private static RunCommandsArguments GetArgsForCommand(Guid powerShellId, out string computerName, out Guid jobInstanceId) - { - RunCommandsArguments args; - ArgsTable.TryGetValue(powerShellId, out args); - - computerName = LocalHost; - - jobInstanceId = args == null - ? Guid.Empty - : args.PSActivityContext.JobInstanceId; - - if (args != null ) - { - if (args.PSActivityContext.HostExtension != null && - args.PSActivityContext.HostExtension.Parameters != null && - args.PSActivityContext.HostExtension.Parameters.ContainsKey("PSComputerName")) - { - string[] computerNames = - (string[]) args.PSActivityContext.HostExtension.Parameters["PSComputerName"]; - - if (computerNames.Length == 1) - { - // this is the workflow fan-out scenario. Use the intended computer - // name in this case - computerName = computerNames[0]; - return args; - } - } - - // looks like an activity level fan-out. If this is a command - // run with a runspace, try to extract the computer name from - // the runspace - switch (args.CommandExecutionType) - { - case CommandRunOutOfProc: - case CommandRunInProc: - case CommandRunRemotely: - computerName = GetComputerNameFromCommand(args.ImplementationContext.PowerShellInstance); - break; - } - } - - if (args != null) - { - ActivityImplementationContext implementationContext = args.ImplementationContext; - - if (implementationContext != null) - { - if (implementationContext.PSRemotingBehavior == RemotingBehavior.Custom) - { - if (implementationContext.PSComputerName != null && - implementationContext.PSComputerName.Length != 0) - { - computerName = implementationContext.PSComputerName[0]; - } - } - } - } - - return args; - } - - internal static void AddIdentifierInfoToErrorRecord(ErrorRecord errorRecord, string computerName, Guid jobInstanceId) - { - RemotingErrorRecord remoteErrorRecord = errorRecord as RemotingErrorRecord; - - if (remoteErrorRecord != null) return; - - if (errorRecord.ErrorDetails == null) - { - errorRecord.ErrorDetails = new ErrorDetails(String.Empty); - errorRecord.ErrorDetails.RecommendedAction = AddIdentifierInfoToString(jobInstanceId, computerName, - errorRecord.ErrorDetails. - RecommendedAction); - } - else - { - errorRecord.ErrorDetails.RecommendedAction = - AddIdentifierInfoToString(jobInstanceId, computerName, errorRecord.ErrorDetails.RecommendedAction); - } - } - - internal static string AddIdentifierInfoToString(Guid instanceId, string computerName, string message) - { - Guid jobInstanceId; - string originalComputerName; - string originalMessage; - string messageToAppend = StringContainsIdentifierInfo(message, out jobInstanceId, out originalComputerName, - out originalMessage) - ? originalMessage - : message; - - var newMessage = new StringBuilder(instanceId.ToString()); - newMessage.Append(":["); - newMessage.Append(computerName); - newMessage.Append("]:"); - newMessage.Append(messageToAppend); - return newMessage.ToString(); - } - - private const string MessagePattern = @"^([\d\w]{8}\-[\d\w]{4}\-[\d\w]{4}\-[\d\w]{4}\-[\d\w]{12}:\[.*\]:).*"; - private static readonly char[] Delimiter = new[]{':'}; - - private static bool StringContainsIdentifierInfo(string message, out Guid jobInstanceId, out string computerName, out string originalString) - { - jobInstanceId = Guid.Empty; - computerName = string.Empty; - originalString = string.Empty; - - if (string.IsNullOrEmpty(message)) return false; - - - if (!Regex.IsMatch(message, MessagePattern)) - return false; - - String[] parts = message.Split(Delimiter, 3); - if (parts.Length != 3) return false; - - if (!Guid.TryParse(parts[0], out jobInstanceId)) - jobInstanceId = Guid.Empty; - - computerName = parts[1]; - originalString = parts[2].Trim(); - return true; - } - - private const string LocalHost = "localhost"; - private static String GetComputerNameFromCommand(System.Management.Automation.PowerShell commandToRun) - { - Runspace runspace = commandToRun.Runspace; - - return runspace.ConnectionInfo == null ? LocalHost : runspace.ConnectionInfo.ComputerName; - } - - private readonly static ConcurrentDictionary ArgsTable = - new ConcurrentDictionary(); - - private static void HandleErrorDataAdded(object sender, DataAddedEventArgs e) - { - RunCommandsArguments args; - ArgsTable.TryGetValue(e.PowerShellInstanceId, out args); - if (args == null) return; - - bool hasErrorMerged = args.PSActivityContext.MergeErrorToOutput; - - if (hasErrorMerged) - { - MergeError_DataAdded(sender, e, args.PSActivityContext.errors); - } - } - - internal static void RemoveHandlersFromStreams(System.Management.Automation.PowerShell commandToRun, RunCommandsArguments args) - { - if (commandToRun == null || args == null) - return; - - bool hasErrorMerged = args.PSActivityContext.MergeErrorToOutput; - - if (hasErrorMerged) - { - commandToRun.Streams.Error.DataAdded -= HandleErrorDataAdded; - } - - if (args.PSActivityContext.Output != null) - args.PSActivityContext.Output.DataAdding -= HandleOutputDataAdding; - commandToRun.Streams.Error.DataAdding -= HandleErrorDataAdding; - commandToRun.Streams.Progress.DataAdding -= HandleProgressDataAdding; - commandToRun.Streams.Verbose.DataAdding -= HandleInformationalRecordDataAdding; - commandToRun.Streams.Warning.DataAdding -= HandleInformationalRecordDataAdding; - commandToRun.Streams.Debug.DataAdding -= HandleInformationalRecordDataAdding; - commandToRun.Streams.Information.DataAdding -= HandleInformationDataAdding; - - RunCommandsArguments arguments; - ArgsTable.TryRemove(commandToRun.InstanceId, out arguments); - } - - private static void MergeError_DataAdded(object sender, DataAddedEventArgs e, PSDataCollection errors) - { - if (errors != null) - { - // Don't use the index from "e" because that may cause race condition. - // It's safe to always remove the first item. - errors.RemoveAt(0); - } - } - - private static void PowerShellInvocation_ErrorAdding(object sender, DataAddingEventArgs e, HostSettingCommandMetadata commandMetadata, PSDataCollection output) - { - ErrorRecord errorRecord = e.ItemAdded as ErrorRecord; - - if (errorRecord != null) - { - if (commandMetadata != null) - { - ScriptPosition scriptStart = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.StartLineNumber, - commandMetadata.StartColumnNumber, - null); - ScriptPosition scriptEnd = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.EndLineNumber, - commandMetadata.EndColumnNumber, - null); - ScriptExtent extent = new ScriptExtent(scriptStart, scriptEnd); - - if (errorRecord.InvocationInfo != null) - { - errorRecord.InvocationInfo.DisplayScriptPosition = extent; - } - } - - if (output != null) - { - output.Add(PSObject.AsPSObject(errorRecord)); - } - } - } - - #endregion Object Decoration - - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This methods executes on a WinRM thread - /// - internal static void PowerShellInvocationCallback(IAsyncResult asyncResult) - { - object asyncState = asyncResult.AsyncState; - Dbg.Assert(asyncState != null, "AsyncState not returned correctly by PowerShell"); - RunCommandsArguments args = asyncState as RunCommandsArguments; - Dbg.Assert(args != null, "AsyncState casting to RunCommandsArguments failed"); - - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback for Executing command using PowerShell - either inproc or remote"); - bool attemptRetry = false; - try - { - if (CheckForCancel(psActivityContext)) return; - commandToRun.EndInvoke(asyncResult); - - // Do any required cleanup here... - implementationContext.CleanUp(); - - if (commandToRun.HadErrors) - { - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Errors occurred executing the command.")); - psActivityContext.Failed = true; - } - } - catch (Exception e) - { - attemptRetry = ProcessException(args, e); - } - finally - { - if (attemptRetry) - { - Interlocked.Decrement(ref psActivityContext.CommandsRunningCount); - BeginActionRetry(args); - } - else - { - ReleaseResourcesAndCheckForEnd(commandToRun, args, true, false); - } - } - } - } - - /// - /// - /// - /// - /// - /// - /// THREADING CONTRACT: - /// This methods executes on a WinRM thread - /// - private static void RunspaceDisconnectedCallback(RunCommandsArguments args, Exception runspaceDisconnectedException) - { - PSActivityContext psActivityContext = args.PSActivityContext; - ActivityImplementationContext implementationContext = args.ImplementationContext; - System.Management.Automation.PowerShell commandToRun = implementationContext.PowerShellInstance; - Dbg.Assert(runspaceDisconnectedException != null, "Exception needs to be passed to RunspaceDisconnectedCallback"); - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - tracer.WriteMessage("Executing callback when remote runspace got disconnected"); - bool attemptRetry = false; - try - { - if (CheckForCancel(psActivityContext)) return; - - // Do any required cleanup here... - implementationContext.CleanUp(); - - // if there are any helper commands they need to be disposed - if (args.HelperCommand != null) - { - args.HelperCommand.Dispose(); - args.HelperCommand = null; - } - - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Runspace disconnected is treated as an errors in executing the command.")); - psActivityContext.Failed = true; - - throw runspaceDisconnectedException; - } - catch (Exception e) - { - attemptRetry = HandleRunOneCommandException(args, e); - if (attemptRetry) - BeginActionRetry(args); - } - finally - { - RemoveHandlersFromStreams(commandToRun, args); - RunOneCommandFinally(args, attemptRetry); - tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity: Finished running command.")); - DecrementRunningCountAndCheckForEnd(psActivityContext); - } - } - } - - // Handle a failure. Check if the attempt has gone over the number of allowed attempts, - // and if not, sleep for the retry delay. - static private bool HandleFailure(int attempts, uint? retryCount, uint? retryInterval, - ActivityImplementationContext implementationContext, string errorId, Exception e, PSActivityContext psActivityContext) - { - bool attemptRetry = false; - - // See if the attempt has gone over the quota - if (attempts > retryCount.GetValueOrDefault(0)) - { - // If this was a multi-machine activity, this is an error, not an exception. - if ((implementationContext.PSComputerName != null) && (implementationContext.PSComputerName.Length > 1)) - { - if (e != null) - { - WriteError(e, errorId, ErrorCategory.InvalidResult, implementationContext.PowerShellInstance.Runspace.ConnectionInfo, psActivityContext); - } - } - else - { - if (e != null) - { - // If they wanted to suspend on error, do so. - if (psActivityContext.ParameterDefaults.ContainsKey(Constants.PSSuspendOnError) && - (((bool) psActivityContext.ParameterDefaults[Constants.PSSuspendOnError]) == true)) - { - psActivityContext.SuspendOnError = true; - WriteError(e, errorId, ErrorCategory.InvalidResult, implementationContext.PowerShellInstance.Runspace.ConnectionInfo, psActivityContext); - } - else - { - lock (psActivityContext.exceptions) - { - psActivityContext.exceptions.Add(e); - } - } - } - } - } - // We should do a retry - else - { - // Write / Log that an activity retry was required. - if (psActivityContext.progress != null) - { - string progressActivity = ((Activity) psActivityContext.ActivityObject).DisplayName; - - if (string.IsNullOrEmpty(progressActivity)) - progressActivity = psActivityContext.ActivityType.Name; - - string retryMessage = String.Format(CultureInfo.CurrentCulture, Resources.RetryingAction, attempts); - - ProgressRecord progressRecord = new ProgressRecord(0, progressActivity, retryMessage); - lock (psActivityContext.progress) - { - psActivityContext.progress.Add(progressRecord); - } - } - - if (e != null) - { - WriteError(e, errorId, ErrorCategory.InvalidResult, implementationContext.PowerShellInstance.Runspace.ConnectionInfo, psActivityContext); - } - - // Reschedule the command for another attempt. - if (!psActivityContext.IsCanceled) - attemptRetry = true; - - // Wait for the retry delay - for (int currentRetry = 0; currentRetry < retryInterval.GetValueOrDefault(1); currentRetry++) - { - if (CheckForCancel(psActivityContext)) - break; - Thread.Sleep(1000); - } - } - - return attemptRetry; - } - - /// - /// Cancel the running activity - /// - /// The NativeActivityContext provided by the workflow. - protected override void Cancel(NativeActivityContext context) - { - try - { - if (this.bookmarking.Get(context) == true) - { - NoPersistHandle handle = this.noPersistHandle.Get(context); - handle.Enter(context); - } - - PSActivityContext psActivityContextInstance = null; - - HostParameterDefaults hostValues = context.GetExtension(); - - if ((hostValues != null) && (hostValues.AsyncExecutionCollection != null)) - { - Dictionary asyncExecutionCollection = hostValues.AsyncExecutionCollection; - - if (asyncExecutionCollection.ContainsKey(context.ActivityInstanceId)) - { - psActivityContextInstance = asyncExecutionCollection[context.ActivityInstanceId]; - asyncExecutionCollection.Remove(context.ActivityInstanceId); - } - - } - - if (psActivityContextInstance == null) - { - psActivityContextInstance = psActivityContextImplementationVariable.Get(context); - } - - psActivityContextInstance.IsCanceled = true; - psActivityContextImplementationVariable.Set(context, psActivityContextInstance); - - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity: Executing cancel request.")); - - if (psActivityContextInstance.commandQueue != null && !psActivityContextInstance.commandQueue.IsEmpty) - { - foreach(ActivityImplementationContext activityImplementationContext in psActivityContextInstance.commandQueue.ToArray() ) - { - RunCommandsArguments args = null; - ArgsTable.TryGetValue(activityImplementationContext.PowerShellInstance.InstanceId, out args); - - if (args != null) - { - RemoveHandlersFromStreams(activityImplementationContext.PowerShellInstance, args); - } - } - } - - if (psActivityContextInstance.runningCommands != null && psActivityContextInstance.runningCommands.Count > 0) - { - lock (psActivityContextInstance.runningCommands) - { - foreach (System.Management.Automation.PowerShell command in psActivityContextInstance.runningCommands.Keys) - { - RunCommandsArguments args = null; - ArgsTable.TryGetValue(command.InstanceId, out args); - - if (args != null) - { - RemoveHandlersFromStreams(command, args); - } - } - } - } - - psActivityContextInstance.Cancel(); - - } - finally - { - // Marking the workflow state as cancelled. - context.MarkCanceled(); - } - } - - private static void UnregisterAndReleaseRunspace(Runspace runspace, PSWorkflowHost workflowHost, PSActivityContext psActivityContext) - { - Dbg.Assert(runspace.ConnectionInfo != null, "Only remote runspaces can be passed to UnregisterAndReleaseRunspace"); - Dbg.Assert(workflowHost != null, "For remote types PSWorkflowHost should be passed"); - Dbg.Assert(psActivityContext != null, "For remote types, activity context must be passed"); - RunCommandsArguments args; - ArgsTableForRunspaces.TryRemove(runspace.InstanceId, out args); - args = null; - if (psActivityContext.HandleRunspaceStateChanged != null) - runspace.StateChanged -= psActivityContext.HandleRunspaceStateChanged; - workflowHost.RemoteRunspaceProvider.ReleaseRunspace(runspace); - } - - private static void CloseRunspace(Runspace runspace, int commandType = CommandRunInProc, PSWorkflowHost workflowHost = null, PSActivityContext psActivityContext = null) - { - switch (commandType) - { - case CommandRunInProc: - Dbg.Assert(workflowHost != null, "For commands to run in proc PSWorkflowHost should be passed"); - workflowHost.LocalRunspaceProvider.ReleaseRunspace(runspace); - break; - - case CommandRunRemotely: - UnregisterAndReleaseRunspace(runspace, workflowHost, psActivityContext); - break; - - case CimCommandRunInProc: - Dbg.Assert(workflowHost != null, "For CIM commands to run in proc, PSWorkflowHost should be passed"); - workflowHost.LocalRunspaceProvider.ReleaseRunspace(runspace); - break; - - case CommandRunOutOfProc: - case RunInProcNoRunspace: - { - // do nothing - } - break; - default: - Dbg.Assert(false, "Attempting to close runspace for a command type that is not supported"); - break; - } - } - - internal static void CloseRunspaceAndDisposeCommand(System.Management.Automation.PowerShell currentCommand, PSWorkflowHost WorkflowHost, PSActivityContext psActivityContext, int commandType) - { - Dbg.Assert(commandType != RunInProcNoRunspace, "Disposing runspaces should not occur for WMI"); - if (!currentCommand.IsRunspaceOwner && (currentCommand.Runspace.RunspaceStateInfo.State == RunspaceState.Opened || currentCommand.Runspace.RunspaceStateInfo.State == RunspaceState.Disconnected)) - CloseRunspace(currentCommand.Runspace, commandType, WorkflowHost, psActivityContext); - currentCommand.Dispose(); - } - - /// - /// Retrieves the stream and ubiquitous parameter information from the hosting application. - /// These must be passed in as "Streams" and "UbiquitousParameters", respectively. - /// - /// The metadata provided by the hosting application. - protected override void CacheMetadata(NativeActivityMetadata metadata) - { - base.CacheMetadata(metadata); - - metadata.AddImplementationVariable(this.bookmarking); - metadata.AddImplementationVariable(this.noPersistHandle); - metadata.AddImplementationChild(this.cancelTimer); - metadata.AddImplementationVariable(psRunningTimeoutDelayActivityInstanceVar); - metadata.AddImplementationChild(this.terminateActivity); - metadata.AddImplementationChild(this.suspendActivity); - metadata.AddDefaultExtensionProvider(() => new PSWorkflowInstanceExtension()); - - this.ParameterDefaults = new Variable>(); - metadata.AddImplementationVariable(this.ParameterDefaults); - - Tracer.WriteMessage(this.GetType().Name, "CacheMetadata", Guid.Empty, - "Adding PowerShell specific extensions to metadata, CommonParameters are {0} available.", - "not"); - - metadata.AddImplementationVariable(psActivityContextImplementationVariable); - } - - /// - /// The event fired when the PSActivity-derived activity has initialized its its instance - /// of System.Management.Automation.PowerShell, but has not yet invoked its action. This event - /// is for diagnostic, tracing, and testing purposes. - /// - internal static event ActivityCreatedEventHandler ActivityCreated; - - private static void OnActivityCreated(Object sender, ActivityCreatedEventArgs e) - { - if (ActivityCreated != null) - { - ActivityCreated(sender, e); - } - } - - internal static bool IsActivityInlineScript(Activity activity) - { - return String.Equals(activity.GetType().FullName, "Microsoft.PowerShell.Activities.InlineScript", StringComparison.OrdinalIgnoreCase); - } - - internal bool RunWithCustomRemoting(ActivityContext context) - { - if (typeof(PSRemotingActivity).IsAssignableFrom(GetType())) - { - PSRemotingActivity remotingActivity = (PSRemotingActivity) this; - if (remotingActivity.PSRemotingBehavior.Get(context) == RemotingBehavior.Custom) - return true; - } - - return false; - } - - #region Check In-Proc vs Out-of-Proc - - /// - /// Determine if this activity should be run in or out of process - /// when run locally/ - /// - /// The native activity context for this workflow instance - /// True if it should be run in process with the workflow engine. - protected bool GetRunInProc(ActivityContext context) - { - HostParameterDefaults defaults = context.GetExtension(); - if (this is PSGeneratedCIMActivity) - { - return true; - } - - // Look to see if there is a PSRunInProc variable in scope... - foreach (System.ComponentModel.PropertyDescriptor property in context.DataContext.GetProperties()) - { - if (string.Equals(property.DisplayName, WorkflowPreferenceVariables.PSRunInProcessPreference, StringComparison.OrdinalIgnoreCase)) - { - object parentId = property.GetValue(context.DataContext); - if (parentId != null) - { - bool variableValue; - if (LanguagePrimitives.TryConvertTo(parentId, CultureInfo.InvariantCulture, out variableValue)) - { - return variableValue; - } - } - } - } - - PSActivityContext activityContext = psActivityContextImplementationVariable.Get(context); - PSWorkflowHost workflowHost = GetWorkflowHost(defaults); - - return workflowHost.PSActivityHostController.RunInActivityController(this); - } - - internal static PSWorkflowHost GetWorkflowHost(HostParameterDefaults defaults) - { - PSWorkflowHost _psWorkflowHost = null; - - if ((defaults != null) && (defaults.Runtime != null)) - { - PSWorkflowHost workflowHost = defaults.Runtime; - Interlocked.CompareExchange(ref _psWorkflowHost, workflowHost, null); - - if (_psWorkflowHost != workflowHost) - { - System.Diagnostics.Debug.Assert(false, "Workflow host has been set before the incoming value was processed"); - } - } - if (_psWorkflowHost == null) - { - _psWorkflowHost = DefaultWorkflowHost.Instance; - } - - return _psWorkflowHost; - } - - #endregion Check In-Proc vs Out-of-Proc - } - - - /// - /// The delegate invoked when an activity is created. - /// - /// The PSActivity instance being invoked. - /// The ActivityCreatedEventArgs associated with this invocation. - internal delegate void ActivityCreatedEventHandler(object sender, ActivityCreatedEventArgs e); - - /// - /// Holds the event arguments when a new PSActivity instance is created. - /// - internal class ActivityCreatedEventArgs : EventArgs - { - /// - /// Creates a new ActivityCreatedEventArgs instance. - /// - /// The instance of System.Management.Automation.PowerShell the activity has prepared. - internal ActivityCreatedEventArgs(System.Management.Automation.PowerShell instance) - { - PowerShellInstance = instance; - } - - /// - /// The instance of System.Management.Automation.PowerShell the activity has prepared. - /// - public System.Management.Automation.PowerShell PowerShellInstance - { - get; - set; - } - } - - /// - /// Holds an instance of System.Management.Automation.PowerShell, and the context it needs to run. - /// - public class ActivityImplementationContext - { - /// - /// The instance of System.Management.Automation.PowerShell the activity has prepared. - /// - public System.Management.Automation.PowerShell PowerShellInstance - { - get; - set; - } - - /// - /// Any context required by the command. - /// - public Object WorkflowContext - { - get; - set; - } - - /// - /// context id. - /// - internal int Id - { - get; - set; - } - - /// - /// DisconnectedRunspaceInstanceId - /// - internal Guid DisconnectedRunspaceInstanceId - { - get; - set; - } - - /// - /// DisconnectedRunspaceInstanceId - /// - internal bool EnableRemotingActivityAutoResume - { - get; - set; - } - - /// - /// The Input stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection Input - { - get; - set; - } - - /// - /// The collection to hold the results of the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection Result - { - get; - set; - } - - /// - /// The Error stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSError - { - get; - set; - } - - /// - /// The Progress stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSProgress - { - get; - set; - } - - /// - /// The Verbose stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSVerbose - { - get; - set; - } - - /// - /// The Debug stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSDebug - { - get; - set; - } - - /// - /// The Warning stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSWarning - { - get; - set; - } - - /// - /// The Information stream / collection for the activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", - "CA2227:CollectionPropertiesShouldBeReadOnly", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public PSDataCollection PSInformation - { - get; - set; - } - - /// - /// The computer name to invoke this activity on. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", - "CA1819:PropertiesShouldNotReturnArrays", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public string[] PSComputerName - { - get; - set; - } - - /// - /// Defines the credential to use in the remote connection. - /// - public PSCredential PSCredential - { - get; - set; - } - - /// - /// Defines the remoting behavior to use when invoking this activity. - /// - public RemotingBehavior PSRemotingBehavior { get; set; } - - /// - /// Defines the number of retries that the activity will make to connect to a remote - /// machine when it encounters an error. The default is to not retry. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public uint? PSConnectionRetryCount { get; set; } - - /// - /// The port to use in a remote connection attempt. The default is: - /// HTTP: 5985, HTTPS: 5986. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public uint? PSPort { get; set; } - - /// - /// Determines whether to use SSL in the connection attempt. The default is false. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public bool? PSUseSsl { get; set; } - - /// - /// Determines whether to allow redirection by the remote computer. The default is false. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public bool? PSAllowRedirection { get; set; } - - /// - /// Defines the remote application name to connect to. The default is "wsman". - /// - public string PSApplicationName { get; set; } - - /// - /// Defines the remote configuration name to connect to. The default is "Microsoft.PowerShell". - /// - public string PSConfigurationName { get; set; } - - /// - /// Defines the fully-qualified remote URI to connect to. When specified, the PSComputerName, - /// PSApplicationName, PSConfigurationName, and PSPort are not used. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", - "CA1819:PropertiesShouldNotReturnArrays", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public string[] PSConnectionUri { get; set; } - - /// - /// Defines the authentication type to be used in the remote connection. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public AuthenticationMechanism? PSAuthentication { get; set; } - - /// - /// Defines the certificate thumbprint to be used in the remote connection. - /// - public string PSCertificateThumbprint { get; set; } - - /// - /// Defines any session options to be used in the remote connection. - /// - public System.Management.Automation.Remoting.PSSessionOption PSSessionOption { get; set; } - - - /// - /// Forces the activity to return non-serialized objects. Resulting objects - /// have functional methods and properties (as opposed to serialized versions - /// of them), but will not survive persistence when the Workflow crashes or is - /// persisted. - /// - public bool? PSDisableSerialization - { - get; - set; - } - - /// - /// Forces the activity to not call the persist functionality, which will be responsible for - /// persisting the workflow state onto the disk. - /// - public bool? PSPersist - { - get; - set; - } - - /// - /// Determines whether to append output to Result. - /// - public bool? AppendOutput - { - get; - set; - } - - /// - /// Determines whether to merge the error data to the output stream - /// - public bool? MergeErrorToOutput - { - get; - set; - } - - /// - /// Defines the maximum amount of time, in seconds, that this activity may run. - /// The default is unlimited. - /// - public uint? PSActionRunningTimeoutSec - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between connection retry attempts. - /// The default is one second. - /// - public uint? PSConnectionRetryIntervalSec - { - get; - set; - } - - /// - /// Defines the number of retries that the activity will make when it encounters - /// an error during execution of its action. The default is to not retry. - /// - public uint? PSActionRetryCount - { - get; - set; - } - - /// - /// Defines the delay, in seconds, between action retry attempts. - /// The default is one second. - /// - public uint? PSActionRetryIntervalSec - { - get; - set; - } - - /// - /// Defines the PSProgressMessage. - /// - public string PSProgressMessage - { - get; - set; - } - - /// - /// The connection info to use for this command (may be null) - /// - public WSManConnectionInfo ConnectionInfo - { - get; - set; - } - - /// - /// This the list of module names (or paths) that are required to run this Activity successfully. - /// The default is null. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", - "CA1819:PropertiesShouldNotReturnArrays", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public string[] PSRequiredModules - { - get; - set; - } - - /// - /// The path that the workflow was imported from. - /// - public string PSWorkflowPath - { - get; - set; - } - - /// - /// Determines whether to emit verbose output of the activity. - /// - public bool? Verbose - { - get; - set; - } - - /// - /// Determines whether to emit debug output of the activity. - /// - public bool? Debug - { - get; - set; - } - - /// - /// Determines whether to emit whatif output of the activity. - /// - public bool? WhatIf - { - get; - set; - } - - /// - /// Determines how errors should be handled by the activity. - /// - public ActionPreference? ErrorAction - { - get; - set; - } - - /// - /// Determines how warnings should be handled by the activity. - /// - public ActionPreference? WarningAction - { - get; - set; - } - - /// - /// Determines how information messages should be handled by the activity. - /// - public ActionPreference? InformationAction - { - get; - set; - } - /// - /// Policy for activity host that will execute this activity - /// - public PSActivityEnvironment PSActivityEnvironment - { - get; - set; - } - - /// - /// Specifies the authentication level to be used with the WMI connection. Valid values are: - /// -1: Unchanged - /// 0: Default - /// 1: None (No authentication in performed.) - /// 2: Connect (Authentication is performed only when the client establishes a relationship with the application.) - /// 3: Call (Authentication is performed only at the beginning of each call when the application receives the request.) - /// 4: Packet (Authentication is performed on all the data that is received from the client.) - /// 5: PacketIntegrity (All the data that is transferred between the client and the application is authenticated and verified.) - /// 6: PacketPrivacy (The properties of the other authentication levels are used, and all the data is encrypted.) - /// - public AuthenticationLevel PSAuthenticationLevel { get; set; } - - /// - /// Specifies the impersonation level to use. Valid values are: - /// 0: Default (reads the local registry for the default impersonation level , which is usually set to "3: Impersonate".) - /// 1: Anonymous (Hides the credentials of the caller.) - /// 2: Identify (Allows objects to query the credentials of the caller.) - /// 3: Impersonate (Allows objects to use the credentials of the caller.) - /// 4: Delegate (Allows objects to permit other objects to use the credentials of the caller.) - /// - public ImpersonationLevel Impersonation { get; set; } - - /* - * Enables all the privileges of the current user before the command makes the WMI call. - */ - /// - /// Enables all the privileges of the current user before the command makes the WMI call. - /// - public bool EnableAllPrivileges { get; set; } - - /// - /// Specifies the authority to use to authenticate the WMI connection. You can specify - /// standard NTLM or Kerberos authentication. To use NTLM, set the authority setting - /// to ntlmdomain:"DomainName", where "DomainName" identifies a valid NTLM domain name. - /// To use Kerberos, specify kerberos:"DomainName>\ServerName". You cannot include - /// the authority setting when you connect to the local computer. - /// - public string Authority { get; set; } - - /// - /// When used with the Class parameter, this parameter specifies the WMI repository namespace - /// where the referenced WMI class is located. When used with the List parameter, it specifies - /// the namespace from which to gather WMI class information. - /// summary> - public string Namespace { get; set; } - - /// - /// Specifies the preferred locale for WMI objects. Specify the value of the Locale - /// parameter as an array in the MS_"LCID" format in the preferred order . - /// - public string Locale { get; set; } - - /// - /// CIM Sessions to use for this activity. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", - "CA1819:PropertiesShouldNotReturnArrays", - Justification = "This is needs to mimic the properties of the PSActivity class.")] - public CimSession[] CimSession { get; set; } - - - /// - /// Perform any cleanup activities needed by this activity implementation - /// - public virtual void CleanUp() - { - } - } - - class RetryCount - { - //internal int ConnectionAttempts { get; set; } - internal int ActionAttempts - { - get; - set; - } - } - - /// - /// Defining resuming extension. - /// - public class PSWorkflowInstanceExtension : IWorkflowInstanceExtension - { - private WorkflowInstanceProxy instance; - - /// - /// Get all additional extensions. - /// - /// Returns no extensions. - public IEnumerable GetAdditionalExtensions() - { - return null; - } - - /// - /// Set the instance of the workflow. - /// - /// The workflow instance proxy. - public void SetInstance(WorkflowInstanceProxy instance) - { - this.instance = instance; - } - - /// - /// Begin resuming book mark. - /// - /// The bookmark where it will be resumed. - /// The value which need to be passed to the bookmark. - /// The call back function when resuming the bookmark. - /// The state of the async call. - /// Returns the result of async call. - public IAsyncResult BeginResumeBookmark(Bookmark bookmark, object value, AsyncCallback callback, object state) - { - return instance.BeginResumeBookmark(bookmark, value, callback, state); - } - - /// - /// End resuming bookmark. - /// - /// The result of async all. - /// Returns the bookmark resumption result. - public BookmarkResumptionResult EndResumeBookmark(IAsyncResult asyncResult) - { - return instance.EndResumeBookmark(asyncResult); - } - } - - /// - /// Stores information about an activity argument - /// - public sealed class PSActivityArgumentInfo - { - /// - /// The name of the argument. - /// - public string Name { get; set; } - - /// - /// The actual argument. - /// - public Argument Value { get; set; } - } - - /// - /// Abstract base containing the common members and invocation code for the WMI cmdlets. - /// - public abstract class WmiActivity : PSActivity - { - /// - /// The computer name to invoke this activity on. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSComputerName - { - get; - set; - } - - /// - /// Defines the credential to use in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCredential - { - get; - set; - } - - /// - /// Specifies the authentication level to be used with the WMI connection. Valid values are: - /// -1: Unchanged - /// 0: Default - /// 1: None (No authentication in performed.) - /// 2: Connect (Authentication is performed only when the client establishes a relationship with the application.) - /// 3: Call (Authentication is performed only at the beginning of each call when the application receives the request.) - /// 4: Packet (Authentication is performed on all the data that is received from the client.) - /// 5: PacketIntegrity (All the data that is transferred between the client and the application is authenticated and verified.) - /// 6: PacketPrivacy (The properties of the other authentication levels are used, and all the data is encrypted.) - /// - [ConnectivityCategory] - [DefaultValue(null)] - public AuthenticationLevel PSAuthenticationLevel { get; set; } - - /// - /// Specifies the impersonation level to use. Valid values are: - /// 0: Default (reads the local registry for the default impersonation level , which is usually set to "3: Impersonate".) - /// 1: Anonymous (Hides the credentials of the caller.) - /// 2: Identify (Allows objects to query the credentials of the caller.) - /// 3: Impersonate (Allows objects to use the credentials of the caller.) - /// 4: Delegate (Allows objects to permit other objects to use the credentials of the caller.) - /// - [ConnectivityCategory] - [DefaultValue(null)] - public ImpersonationLevel Impersonation { get; set; } - - /* - * Enables all the privileges of the current user before the command makes the WMI call. - */ - /// - /// Enables all the privileges of the current user before the command makes the WMI call. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public bool EnableAllPrivileges { get; set; } - - /// - /// Specifies the authority to use to authenticate the WMI connection. You can specify - /// standard NTLM or Kerberos authentication. To use NTLM, set the authority setting - /// to ntlmdomain:"DomainName", where "DomainName" identifies a valid NTLM domain name. - /// To use Kerberos, specify kerberos:"DomainName>\ServerName". You cannot include - /// the authority setting when you connect to the local computer. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public string Authority { get; set; } - - /// - /// When used with the Class parameter, this parameter specifies the WMI repository namespace - /// where the referenced WMI class is located. When used with the List parameter, it specifies - /// the namespace from which to gather WMI class information. - /// summary> - [BehaviorCategory] - [DefaultValue(null)] - public InArgument Namespace { get; set; } - - /// - /// Specifies the preferred locale for WMI objects. Specify the value of the Locale - /// parameter as an array in the MS_"LCID" format in the preferred order . - /// - [BehaviorCategory] - [DefaultValue(null)] - public string Locale { get; set; } - - /// - /// Generic version of the function to handle value types - /// - /// The type of the intended argument - /// - /// - /// - protected T GetUbiquitousParameter(string parameterName, Dictionary parameterDefaults) - { - if (ParameterDefaults != null && parameterDefaults.ContainsKey(parameterName)) - return (T)parameterDefaults[parameterName]; - else - return default(T); - } - - /// - /// Sets to execute the command that was passed in. - /// - /// - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected System.Management.Automation.PowerShell GetWmiCommandCore(NativeActivityContext context, string name) - { - System.Management.Automation.PowerShell command; - command = System.Management.Automation.PowerShell.Create().AddCommand(name); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: WMI Command '{1}'.", - context.ActivityInstanceId, name)); - - - if (Impersonation != ImpersonationLevel.Default) - { - command.AddParameter("Impersonation", Impersonation); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Impersonation", Impersonation)); - - } - - Dictionary parameterDefaults = context.GetValue>(this.ParameterDefaults); - - if (PSAuthenticationLevel != AuthenticationLevel.Default) - { - command.AddParameter("Authentication", PSAuthenticationLevel); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Authentication", PSAuthenticationLevel)); - } - else if (GetUbiquitousParameter("PSAuthenticationLevel", parameterDefaults) != AuthenticationLevel.Default) - { - var authLevel = GetUbiquitousParameter("PSAuthenticationLevel", parameterDefaults); - command.AddParameter("Authentication", authLevel); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to {2} from ubiquitous parameters.", - context.ActivityInstanceId, "AuthenticationLevel", authLevel)); - } - - if (Locale != null) - { - command.AddParameter("Locale", Locale); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Locale", Locale)); - - } - - if (EnableAllPrivileges) - { - command.AddParameter("EnableAllPrivileges", EnableAllPrivileges); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "EnableAllPrivileges", EnableAllPrivileges)); - } - - if (Authority != null) - { - command.AddParameter("Authority", Authority); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "PowerShell activity ID={0}: Setting parameter {1} to {2}.", context.ActivityInstanceId, "Authority", Authority)); - } - - if (Namespace.Get(context) != null) - { - command.AddParameter("Namespace", Namespace.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Namespace", Namespace.Get(context))); - } - - // WMI does it's own remoting so we need to handle the PSCredential/Credential parameter - // explicitly ourselves. - if (PSCredential.Get(context) != null) - { - command.AddParameter("Credential", PSCredential.Get(context)); - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity ID={0}: Setting parameter {1} to {2}.", - context.ActivityInstanceId, "Credential", PSCredential.Get(context))); - } - - return command; - } - - /// - /// Perform necessary steps to prepare the WMI commands - /// - /// The activity context to use - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected override List GetImplementation(NativeActivityContext context) - { - List commands = new List(); - string[] computernames = PSComputerName.Get(context); - - // Configure the remote connectivity options - if (computernames == null || computernames.Length == 0) - { - computernames = new string[] { "localhost" }; - } - - foreach (string computername in computernames) - { - // Create the PowerShell instance, and add the command to it. - ActivityImplementationContext implementationContext = GetPowerShell(context); - System.Management.Automation.PowerShell invoker = implementationContext.PowerShellInstance; - - // Don't add the computer if it's empty or localhost... - if (!String.IsNullOrEmpty(computername) && !String.Equals(computername, "localhost", StringComparison.OrdinalIgnoreCase)) - { - invoker.AddParameter("ComputerName", computername); - } - - commands.Add( - new ActivityImplementationContext() { PowerShellInstance = invoker } - ); - } - - return commands; - } - } - - /// - /// Implementation of ICommandRuntime for running the WMI cmdlets in - /// workflow without PowerShell. - /// - internal class DirectExecutionActivitiesCommandRuntime : ICommandRuntime - { - - /// - /// Constructs an instance of the default ICommandRuntime object - /// that will write objects into the arraylist that was passed. - /// - public DirectExecutionActivitiesCommandRuntime(PSDataCollection output, ActivityImplementationContext implementationContext, Type cmdletType) - { - if (output == null) throw new ArgumentNullException("output"); - if (implementationContext == null) throw new ArgumentNullException("implementationContext"); - if (cmdletType == null) throw new ArgumentNullException("cmdletType"); - - _output = output; - _implementationContext = implementationContext; - _cmdletType = cmdletType; - } - - PSDataCollection _output; - ActivityImplementationContext _implementationContext; - Type _cmdletType; - - /// - /// THe error record stream - /// - public PSDataCollection Error { get; set; } - - /// - /// The progress record stream - /// - public PSDataCollection Progress { get; set; } - - /// - /// The verbose record stream - /// - public PSDataCollection Verbose { get; set; } - - /// - /// The warning record stream - /// - public PSDataCollection Warning { get; set; } - - /// - /// The debug output stream - /// - public PSDataCollection Debug { get; set; } - - /// - /// The information record stream - /// - public PSDataCollection Information { get; set; } - - /// - /// Return the instance of PSHost - null by default. - /// - public PSHost Host { get { return null; } } - - #region Write - /// - /// Implementation of WriteDebug - just discards the input. - /// - /// Text to write - public void WriteDebug(string text) - { - if (Debug == null) - return; - - if (text != null) - { - Debug.Add(new DebugRecord(text)); - } - } - - /// - /// Default implementation of WriteError - if the error record contains - /// an exception then that exception will be thrown. If not, then an - /// InvalidOperationException will be constructed and thrown. - /// - /// Error record instance to process - public void WriteError(ErrorRecord errorRecord) - { - if (Error == null) - return; - - ErrorRecord updatedErrorRecord = new ErrorRecord(errorRecord.Exception, errorRecord.FullyQualifiedErrorId + ',' + _cmdletType.FullName, errorRecord.CategoryInfo.Category, errorRecord.TargetObject); - - ActionPreference preference = (_implementationContext.ErrorAction == null) ? - ActionPreference.Continue : _implementationContext.ErrorAction.Value; - - switch (preference) - { - case ActionPreference.SilentlyContinue: - case ActionPreference.Ignore: - break; - case ActionPreference.Inquire: - case ActionPreference.Continue: - if (errorRecord != null) - { - Error.Add(updatedErrorRecord); - } - break; - case ActionPreference.Stop: - ThrowTerminatingError(updatedErrorRecord); - break; - } - } - - /// - /// Default implementation of WriteObject - adds the object to the arraylist - /// passed to the objects constructor. - /// - /// Object to write - public void WriteObject(object sendToPipeline) - { - _output.Add(PSObject.AsPSObject(sendToPipeline)); - } - - /// - /// Write objects to the output collection - /// - /// Object to write - /// If true, the collection is enumerated, otherwise - /// it's written as a scalar. - /// - public void WriteObject(object sendToPipeline, bool enumerateCollection) - { - if (enumerateCollection) - { - IEnumerator e = LanguagePrimitives.GetEnumerator(sendToPipeline); - if (e == null) - { - WriteObject(sendToPipeline); - } - else - { - while (e.MoveNext()) - { - WriteObject(e.Current); - } - } - } - else - { - WriteObject(sendToPipeline); - } - } - - /// - /// Write a progress record - /// - /// progress record to write. - public void WriteProgress(ProgressRecord progressRecord) - { - WriteProgress(1, progressRecord); - } - - /// - /// Write a progress record, ignore the id field - /// - /// Source ID to write for - /// record to write. - public void WriteProgress(Int64 sourceId, ProgressRecord progressRecord) - { - if (Progress == null) - return; - - if (progressRecord != null) - { - Progress.Add(progressRecord); - } - } - - /// - /// Write a verbose record - /// - /// Text to write. - public void WriteVerbose(string text) - { - if (_implementationContext.Verbose != true) - return; - - if (Verbose == null) - return; - - if (text != null) - { - Verbose.Add(new VerboseRecord(text)); - } - } - - /// - /// Write a warning record - /// - /// Text to write. - public void WriteWarning(string text) - { - if (_implementationContext.WarningAction != ActionPreference.Continue) - return; - - if (Warning == null) - return; - - if (text != null) - { - Warning.Add(new WarningRecord(text)); - } - } - - /// - /// Write a information record - /// - /// Record to write. - public void WriteInformation(InformationRecord record) - { - if (_implementationContext.InformationAction != ActionPreference.Continue) - return; - - if (Information == null) - return; - - if (record != null) - { - Information.Add(record); - } - } - - /// - /// Write command detail info to the eventlog. - /// - /// Text to write. - public void WriteCommandDetail(string text) - { - PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource(); - tracer.WriteMessage(text); - } - - #endregion Write - - #region Should - /// - /// Default implementation - always returns true. - /// - /// ignored - /// true - public bool ShouldProcess(string target) { return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// true - public bool ShouldProcess(string target, string action) { return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// ignored - /// true - public bool ShouldProcess(string verboseDescription, string verboseWarning, string caption) { return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// ignored - /// ignored - /// true - public bool ShouldProcess(string verboseDescription, string verboseWarning, string caption, out ShouldProcessReason shouldProcessReason) { shouldProcessReason = ShouldProcessReason.None; return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// true - public bool ShouldContinue(string query, string caption) { return true; } - /// - /// Default implementation - always returns true. - /// - /// ignored - /// ignored - /// ignored - /// ignored - /// true - public bool ShouldContinue(string query, string caption, ref bool yesToAll, ref bool noToAll) { return true; } - #endregion Should - - #region Transaction Support - /// - /// Returns true if a transaction is available and active. - /// - public bool TransactionAvailable() { return false; } - - /// - /// Gets an object that surfaces the current PowerShell transaction. - /// When this object is disposed, PowerShell resets the active transaction - /// - public PSTransactionContext CurrentPSTransaction - { - get - { - // We want to throw in this situation, and want to use a - // property because it mimics the C# using(TransactionScope ...) syntax - throw new InvalidOperationException(); - } - } - #endregion Transaction Support - - #region Misc - /// - /// Implementation of the dummy default ThrowTerminatingError API - it just - /// does what the base implementation does anyway - rethrow the exception - /// if it exists, otherwise throw an invalid operation exception. - /// - /// The error record to throw - public void ThrowTerminatingError(ErrorRecord errorRecord) - { - if (errorRecord.Exception != null) - { - throw errorRecord.Exception; - } - else - { - throw new System.InvalidOperationException(errorRecord.ToString()); - } - } - #endregion - } - - /// - /// Suspends the current workflow. - /// - internal class SuspendOnError : NativeActivity - { - /// - /// Returns true if the activity can induce an idle. - /// - protected override bool CanInduceIdle { get { return true; } } - - /// - /// Invokes the activity - /// - /// The activity context. - /// True if the given argument is set. - protected override void Execute(NativeActivityContext context) - { - string bookmarkname = PSActivity.PSSuspendBookmarkPrefix; - bookmarkname += Guid.NewGuid().ToString().Replace("-", "_"); - - context.CreateBookmark(bookmarkname, BookmarkResumed); - } - - private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value) - { - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityHostManager.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityHostManager.cs deleted file mode 100644 index 0ad5dadff20..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSActivityHostManager.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Management.Automation; -using System.Threading; -using System.Diagnostics.CodeAnalysis; -using System.Activities; -using Microsoft.PowerShell.Workflow; -using System.Collections.Concurrent; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Class the describes an activity host arguments - /// - internal sealed class PSResumableActivityContext - { - /// - /// PSActivityHostArguments - /// - /// - internal PSResumableActivityContext(PowerShellStreams streams) - { - Streams = streams; - Error = null; - Failed = false; - SupportDisconnectedStreams = true; - } - - /// - /// Gets and sets Streams - /// - internal PowerShellStreams Streams { get; set; } - - /// - /// Gets Errors - /// - internal Exception Error { get; set; } - - /// - /// Get failed - /// - internal bool Failed { get; set; } - - internal bool SupportDisconnectedStreams { get; set; } - } - - /// - /// Class the describes an activity host policy - /// - public sealed class PSActivityEnvironment - { - private readonly Collection _modules = new Collection(); - private readonly Dictionary _variables = new Dictionary(StringComparer.OrdinalIgnoreCase); - - /// - /// Collection of modules that an activity is - /// dependent upon - /// - public Collection Modules - { - get { return _modules; } - } - - /// - /// Collection of variables that an activity is - /// dependent upon - /// - public Dictionary Variables - { - get { return _variables; } - } - } - - #region PSActivityHostController - - /// - /// Activity host manager interface. This interface can be - /// used to implement various activity hosts - /// - public abstract class PSActivityHostController - { - private PSWorkflowRuntime _runtime; - private readonly ConcurrentDictionary _inProcActivityLookup = new ConcurrentDictionary(); - - /// - /// Runtime should be provided for accessing the runtime activity mode - /// - protected PSActivityHostController(PSWorkflowRuntime runtime) - { - _runtime = runtime; - } - - /// - /// Identifies whether the specified activity should run in the activity - /// host or in-proc - /// - /// activity that needs to be verified - /// true, if the activity should run in the activity - /// host - /// false otherwise - [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")] - public virtual bool RunInActivityController(Activity activity) - { - if (activity == null) - { - throw new ArgumentNullException("activity"); - } - - String name = activity.GetType().Name; - - if (_inProcActivityLookup.ContainsKey(name)) return _inProcActivityLookup[name]; - - - ActivityRunMode runMode = _runtime.Configuration.GetActivityRunMode(activity); - bool runInProc = runMode == ActivityRunMode.InProcess; - - return _inProcActivityLookup.GetOrAdd(name, runInProc); - } - } - - /// - /// This class will be used for disconnected execution where the - /// Job Id and bookmark will be used resume the execution of workflow - /// after the completion of activity controller work. - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public abstract class PSResumableActivityHostController : PSActivityHostController - { - /// - /// Default Constructor - /// - /// - protected PSResumableActivityHostController(PSWorkflowRuntime runtime) - : base(runtime) - { - - } - - /// - /// StartResumablePSCommand - /// - /// - /// - /// - /// - /// - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public virtual void StartResumablePSCommand(Guid jobInstanceId, - Bookmark bookmark, - System.Management.Automation.PowerShell command, - PowerShellStreams streams, - PSActivityEnvironment environment, - PSActivity activityInstance) - { - throw new NotImplementedException(); - } - - /// - /// StopResumablePSCommand - /// - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public virtual void StopAllResumablePSCommands(Guid jobInstanceId) - { - throw new NotImplementedException(); - } - - /// - /// This property identifies if the Activity controller is running in disconnected mode - /// or not. If it is running in disconnected mode then all the output and data streams will be - /// proxied as new objects. - /// - public virtual bool SupportDisconnectedPSStreams - { - get - { - return true; - } - } - } - - #endregion -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSCleanupActivity.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSCleanupActivity.cs deleted file mode 100644 index 62d5d003036..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSCleanupActivity.cs +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) 2011 Microsoft Corporation. All rights reserved - */ -using System; -using System.Activities; -using System.Threading; -using System.Management.Automation.Runspaces; -using System.ComponentModel; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Base activity for cleanup activities - /// - public abstract class PSCleanupActivity : PSRemotingActivity - { - /// - /// Creates and returns an empty powershell - /// - /// activity context - /// A new activity implementation context - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - ActivityImplementationContext implementationContext = new ActivityImplementationContext - { - PowerShellInstance = - System.Management.Automation.PowerShell. - Create() - }; - - return implementationContext; - } - - /// - /// Method that needs to be overridden to perform the actual - /// cleanup action - /// - /// RunCommandsArguments - /// callback to call when cleanup - /// is done - /// The signature forces this method to be internal - internal virtual void DoCleanup(RunCommandsArguments args, WaitCallback callback) - { - throw new NotImplementedException(); - } - } - - /// - /// Activity to cleanup all Runspaces (PSRP connections) to - /// a remote machine - /// - public class DisablePSWorkflowConnection : PSCleanupActivity - { - // Arguments - - /// - /// Provides access to the Authentication parameter. - /// - [RequiredArgument] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public InArgument TimeoutSec - { - get; - set; - } - private int timeout = DefaultCleanupWaitTimerIntervalMs; - private const int DefaultCleanupWaitTimerIntervalMs = 5*60*1000; - - /// - /// Set the display name of the activity - /// - public DisablePSWorkflowConnection() - { - DisplayName = "Disable-PSWorkflowConnection"; - } - - /// - /// Creates and returns an empty powershell - /// - /// activity context - /// A new activity implementation context - protected override ActivityImplementationContext GetPowerShell(NativeActivityContext context) - { - if (TimeoutSec.Expression != null) - { - timeout = TimeoutSec.Get(context); - } - - return base.GetPowerShell(context); - } - - /// - /// Method that needs to be overridden to perform the actual - /// cleanup action - /// - /// RunCommandsArguments - /// callback to call when cleanup - /// is done - /// The signature forces this method to be internal - internal override void DoCleanup(RunCommandsArguments args, WaitCallback callback) - { - PSWorkflowHost workflowHost = args.WorkflowHost; - WSManConnectionInfo connectionInfo = - args.ImplementationContext.PowerShellInstance.Runspace.ConnectionInfo as WSManConnectionInfo; - - args.CleanupTimeout = timeout; - - if (connectionInfo == null) - { - if (callback != null) - callback(args); - } - else - { - workflowHost.RemoteRunspaceProvider.RequestCleanup(connectionInfo, callback, args); - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSRemotingActivitiesBase.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSRemotingActivitiesBase.cs deleted file mode 100644 index f789188c6c5..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSRemotingActivitiesBase.cs +++ /dev/null @@ -1,257 +0,0 @@ -using System; -using System.Activities; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Management.Automation; -using System.Management.Automation.Remoting; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.Threading.Tasks; -using System.ComponentModel; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Base class for PowerShell-based workflow activities - /// - public abstract class PSRemotingActivity : PSActivity, IImplementsConnectionRetry - { - /// - /// The computer name to invoke this activity on. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSComputerName - { - get; - set; - } - - /// - /// Defines the credential to use in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCredential - { - get; - set; - } - - /// - /// Defines the remoting behavior to use when invoking this activity. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSRemotingBehavior { get; set; } - - /// - /// Defines the number of retries that the activity will make to connect to a remote - /// machine when it encounters an error. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryCount { get; set; } - - /// - /// Defines the delay, in seconds, between connection retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryIntervalSec { get; set; } - - /// - /// The port to use in a remote connection attempt. The default is: - /// HTTP: 5985, HTTPS: 5986. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSPort { get; set; } - - /// - /// Determines whether to use SSL in the connection attempt. The default is false. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSUseSsl { get; set; } - - /// - /// Determines whether to allow redirection by the remote computer. The default is false. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSAllowRedirection { get; set; } - - /// - /// Defines the remote application name to connect to. The default is "wsman". - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSApplicationName { get; set; } - - /// - /// Defines the remote configuration name to connect to. The default is "Microsoft.PowerShell". - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSConfigurationName { get; set; } - - /// - /// Defines the fully-qualified remote URI to connect to. When specified, the PSComputerName, - /// PSApplicationName, PSConfigurationName, and PSPort are not used. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSConnectionUri { get; set; } - - /// - /// Defines the authentication type to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSAuthentication { get; set; } - - /// - /// Defines the certificate thumbprint to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCertificateThumbprint { get; set; } - - /// - /// Defines any session options to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSSessionOption { get; set; } - - /// - /// Declares whether this command supports its own custom remoting. - /// Commands that support their own custom remoting should return TRUE - /// from this property, and use the PSComputerName parameter as required - /// when the 'PSRemotingBehavior' argument is set to 'Custom'. - /// - protected virtual bool SupportsCustomRemoting { get { return false; } } - - /// - /// Returns TRUE if the PSComputerName argument has been specified, and - /// contains at least one target. - /// - /// The workflow NativeActivityContext - /// - protected bool GetIsComputerNameSpecified(ActivityContext context) - { - return ((PSComputerName.Get(context) != null) && - (PSComputerName.Get(context).Length > 0)); - } - - // Prepare the commands - /// - /// Prepare commands that use PSRP for remoting... - /// - /// The activity context to use - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected override List GetImplementation(NativeActivityContext context) - { - string[] computernames = PSComputerName.Get(context); - string[] connectionUris = PSConnectionUri.Get(context); - PSSessionOption sessionOptions = PSSessionOption.Get(context); - List commands = new List(); - - // Configure the remote connectivity options - RemotingBehavior remotingBehavior = PSRemotingBehavior.Get(context); - if (PSRemotingBehavior.Expression == null) - { - remotingBehavior = RemotingBehavior.PowerShell; - } - - // If they've specified the 'Custom' remoting behavior, ensure the activity - // supports it. - if ((remotingBehavior == RemotingBehavior.Custom) && (!SupportsCustomRemoting)) - { - throw new ArgumentException(Resources.CustomRemotingNotSupported); - } - - if (PSCredential.Get(context) != null && PSAuthentication.Get(context) == AuthenticationMechanism.NegotiateWithImplicitCredential) - { - throw new ArgumentException(Resources.CredentialParameterCannotBeSpecifiedWithNegotiateWithImplicitAuthentication); - } - - // we need connection info to be populated even for the custom remoting case. - // This is because the ComputerName is picked up from the connection info field - if ((remotingBehavior == RemotingBehavior.PowerShell || (IsActivityInlineScript(this) && RunWithCustomRemoting(context))) && - (GetIsComputerNameSpecified(context) || (connectionUris != null && connectionUris.Length > 0))) - { - List connectionInfo = ActivityUtils.GetConnectionInfo( - computernames, - connectionUris, PSCertificateThumbprint.Get(context), PSConfigurationName.Get(context), - PSUseSsl.Get(context), PSPort.Get(context), PSApplicationName.Get(context), - PSCredential.Get(context), PSAuthentication.Get(context).GetValueOrDefault(AuthenticationMechanism.Default), - PSAllowRedirection.Get(context).GetValueOrDefault(false), - sessionOptions); - - foreach (WSManConnectionInfo connection in connectionInfo) - { - CreatePowerShellInstance(context, connection, commands); - } - } - // Configure the local invocation options - else - { - CreatePowerShellInstance(context, null, commands); - } - - return commands; - } - - /// - /// Creates Powershell instance and adds the command to it - /// - /// The activity context to use - /// The wsman connection to use - /// The list of commands - private void CreatePowerShellInstance(NativeActivityContext context, WSManConnectionInfo connection, - List commands) - { - // Create the PowerShell instance, and add the command to it. - ActivityImplementationContext implementationContext = GetPowerShell(context); -#if true - Runspace runspace; - if (connection != null) - { - implementationContext.ConnectionInfo = connection; - runspace = RunspaceFactory.CreateRunspace(connection); - implementationContext.PowerShellInstance.Runspace = runspace; - } - else - { - // if PSComputerName is "" or $null than connection is NULL - UpdateImplementationContextForLocalExecution(implementationContext, context); - } - -#endif - // Add it to the queue of commands to execute. - commands.Add(implementationContext); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSSelfRemotingActivitiesBase.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSSelfRemotingActivitiesBase.cs deleted file mode 100644 index d8d7aaae9b9..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSSelfRemotingActivitiesBase.cs +++ /dev/null @@ -1,854 +0,0 @@ -using System; -using System.Activities; -using System.Collections.Generic; -using System.Management.Automation; -using System.Management.Automation.Remoting; -using System.Management.Automation.Runspaces; -using System.Threading; -using System.ComponentModel; -using Microsoft.Management.Infrastructure; -using Microsoft.Management.Infrastructure.Options; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - /// - /// Base class for activities that can use WsMan directly to contact ta managed node. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CIM")] - public abstract class PSGeneratedCIMActivity : PSActivity, IImplementsConnectionRetry - { - /// - /// The computer name to invoke this activity on. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSComputerName - { - get; - set; - } - - /// - /// Defines the credential to use in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCredential - { - get; - set; - } - - /// - /// Defines the authentication type to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSAuthentication { get; set; } - - - /// - /// Defines the certificate thumbprint to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSCertificateThumbprint { get; set; } - - /// - /// Defines the number of retries that the activity will make to connect to a remote - /// machine when it encounters an error. The default is to not retry. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryCount { get; set; } - - /// - /// Defines the delay, in seconds, between connection retry attempts. - /// The default is one second. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSConnectionRetryIntervalSec { get; set; } - - /// - /// Defines the resource URI used by the CIM cmdlet. - /// - [BehaviorCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument ResourceUri { get; set; } - - /// - /// The port to use in a remote connection attempt. The default is: - /// HTTP: 5985, HTTPS: 5986. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSPort { get; set; } - - /// - /// Determines whether to use SSL in the connection attempt. The default is false. - /// - [ConnectivityCategory] - [DefaultValue(null)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the interaction of PowerShell and Workflow.")] - public InArgument PSUseSsl { get; set; } - - /// - /// Defines any session options to be used in the remote connection. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument PSSessionOption { get; set; } - - /// - /// CIM Sessions to use for this activity. - /// - [ConnectivityCategory] - [DefaultValue(null)] - public InArgument CimSession { get; set; } - - - /// - /// Contains the powershell text defining the mode for this command. - /// - protected abstract string ModuleDefinition { get; } - - /// - /// Returns TRUE if the PSComputerName argument has been specified, and - /// contains at least one target. - /// - /// The workflow NativeActivityContext - /// - protected bool GetIsComputerNameSpecified(ActivityContext context) - { - return ((PSComputerName.Get(context) != null) && - (PSComputerName.Get(context).Length > 0)); - } - - /// - /// Prepare commands that use CIM for remoting... - /// - /// The activity context to use - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - protected override List GetImplementation(NativeActivityContext context) - { - bool needRunspace = !typeof(GenericCimCmdletActivity).IsAssignableFrom(this.GetType()); - string[] computernames = PSComputerName.Get(context); - CimSession[] sessions = this.CimSession.Get(context); - Uri resourceUri = null; - - if (ResourceUri != null) - { - resourceUri = ResourceUri.Get(context); - } - - List commands = new List(); - - // Configure the remote connectivity options... - if (computernames != null && computernames.Length > 0) - { - WSManSessionOptions sessionOptions = new WSManSessionOptions(); - - // Set a timeout on the connection... - uint? timeout = PSActionRunningTimeoutSec.Get(context); - if (timeout.HasValue) - { - sessionOptions.Timeout = TimeSpan.FromSeconds((double)(timeout.Value)); - } - - // See if we should use SSL or not... - bool? useSsl = PSUseSsl.Get(context); - bool sessionOptionUseSsl = false; - - if (useSsl.HasValue) - { - sessionOptions.UseSsl = useSsl.Value; - sessionOptionUseSsl = useSsl.Value; - } - - // Set the port to use - uint? port = PSPort.Get(context); - uint sessionOptionPort = 0; - - if (port.HasValue) - { - sessionOptions.DestinationPort = port.Value; - sessionOptionPort = port.Value; - } - - // Map over options from PSSessionConfig to WSManSessionOptions - PSSessionOption pso = PSSessionOption.Get(context); - if (pso != null) - { - sessionOptions.NoEncryption = pso.NoEncryption; - sessionOptions.CertCACheck = pso.SkipCACheck; - sessionOptions.CertCNCheck = pso.SkipCNCheck; - sessionOptions.CertRevocationCheck = pso.SkipRevocationCheck; - - if (pso.UseUTF16) - sessionOptions.PacketEncoding = PacketEncoding.Utf16; - - if (pso.Culture != null) - sessionOptions.Culture = pso.Culture; - - if (pso.UICulture != null) - sessionOptions.UICulture = pso.UICulture; - - if (pso.ProxyCredential != null) - { - string[] parts = pso.ProxyCredential.UserName.Split('\\'); - string domain, userid; - if (parts.Length < 2) - { - domain = string.Empty; - userid = parts[0]; - } - else - { - domain = parts[0]; - userid = parts[1]; - } - - sessionOptions.AddProxyCredentials( - new CimCredential(ConvertPSAuthenticationMechanismToCimPasswordAuthenticationMechanism(pso.ProxyAuthentication), - domain, userid, pso.ProxyCredential.Password)); - } - - switch (pso.ProxyAccessType) - { - case ProxyAccessType.WinHttpConfig: - sessionOptions.ProxyType = ProxyType.WinHttp; - break; - case ProxyAccessType.AutoDetect: - sessionOptions.ProxyType = ProxyType.Auto; - break; - - case ProxyAccessType.IEConfig: - sessionOptions.ProxyType = ProxyType.InternetExplorer; - break; - } - } - - PSCredential pscreds = PSCredential.Get(context); - string certificateThumbprint = PSCertificateThumbprint.Get(context); - - if (pscreds != null && certificateThumbprint != null) - { - throw new ArgumentException(Resources.CredentialParameterCannotBeSpecifiedWithPSCertificateThumbPrint); - } - - PasswordAuthenticationMechanism passwordAuthenticationMechanism = PasswordAuthenticationMechanism.Default; - AuthenticationMechanism? authenticationMechanism = PSAuthentication.Get(context); - - if (authenticationMechanism.HasValue) - passwordAuthenticationMechanism = ConvertPSAuthenticationMechanismToCimPasswordAuthenticationMechanism(authenticationMechanism.Value); - - - if (certificateThumbprint != null) - { - sessionOptions.AddDestinationCredentials(new CimCredential(CertificateAuthenticationMechanism.Default, certificateThumbprint)); - } - - if (pscreds != null) - { - string[] parts = pscreds.UserName.Split('\\'); - string domain, userid; - if (parts.Length < 2) - { - domain = string.Empty; - userid = parts[0]; - } - else - { - domain = parts[0]; - userid = parts[1]; - } - - sessionOptions.AddDestinationCredentials(new CimCredential(passwordAuthenticationMechanism, domain, userid, pscreds.Password)); - } - - // Create the PowerShell instance, and add the script to it. - if (sessions != null && sessions.Length > 0) - { - foreach (CimSession session in sessions) - { - ActivityImplementationContext configuredCommand = GetPowerShell(context); - - CimActivityImplementationContext activityImplementationContext = - new CimActivityImplementationContext( - configuredCommand, - session.ComputerName, - pscreds, - certificateThumbprint, - authenticationMechanism, - sessionOptionUseSsl, - sessionOptionPort, - pso, - session, - sessionOptions, - ModuleDefinition, - resourceUri); - - commands.Add(activityImplementationContext); - //if (needRunspace) - // GetRunspaceForCimCmdlet(context, activityImplementationContext); - } - } - else if (this.PSCommandName.Equals("CimCmdlets\\New-CimSession", StringComparison.OrdinalIgnoreCase)) - { - // NewCimSession activity is a special one as it creates the required sessions based on number of computers specified in one go. - - ActivityImplementationContext baseContext = GetPowerShell(context); - - CimActivityImplementationContext activityImplementationContext = - new CimActivityImplementationContext(baseContext, - null, // ComputerName - pscreds, - certificateThumbprint, - authenticationMechanism, - sessionOptionUseSsl, - sessionOptionPort, - pso, - null, // session - sessionOptions, - ModuleDefinition, - resourceUri); - - commands.Add(activityImplementationContext); - } - else - { - foreach (string computer in computernames) - { - ActivityImplementationContext baseContext = GetPowerShell(context); - - CimActivityImplementationContext activityImplementationContext = - new CimActivityImplementationContext(baseContext, - computer, - pscreds, - certificateThumbprint, - authenticationMechanism, - sessionOptionUseSsl, - sessionOptionPort, - pso, - null, // session - sessionOptions, - ModuleDefinition, - resourceUri); - - commands.Add(activityImplementationContext); - } - } - } - // Configure the local invocation options - else - { - // Create the PowerShell instance, and add the script to it. - ActivityImplementationContext baseContext = GetPowerShell(context); - CimActivityImplementationContext activityImplementationContext = - new CimActivityImplementationContext(baseContext, - null, // ComputerName - null, // Credential - null, // CertificateThumbprint - AuthenticationMechanism.Default, - false, // UseSsl - 0, // Port - null, // PSSessionOption - null, // Session - null, // CimSessionOptions - ModuleDefinition, - resourceUri); - - commands.Add(activityImplementationContext); - } - - return commands; - } - - internal static PasswordAuthenticationMechanism ConvertPSAuthenticationMechanismToCimPasswordAuthenticationMechanism(AuthenticationMechanism psAuthenticationMechanism) - { - switch (psAuthenticationMechanism) - { - case AuthenticationMechanism.Basic: - return PasswordAuthenticationMechanism.Basic; - - case AuthenticationMechanism.Negotiate: - case AuthenticationMechanism.NegotiateWithImplicitCredential: - return PasswordAuthenticationMechanism.Negotiate; - - case AuthenticationMechanism.Credssp: - return PasswordAuthenticationMechanism.CredSsp; - - case AuthenticationMechanism.Digest: - return PasswordAuthenticationMechanism.Digest; - - case AuthenticationMechanism.Kerberos: - return PasswordAuthenticationMechanism.Kerberos; - - case AuthenticationMechanism.Default: - default: - return PasswordAuthenticationMechanism.Default; - } - } - } - - /// - /// Base class for the built-in generic CIM cmdlets. - /// - public abstract class GenericCimCmdletActivity : PSGeneratedCIMActivity - { - /// - /// For these cmdlets, there is no defining text. - /// - protected override string ModuleDefinition { get { return string.Empty; } } - - /// - /// The .NET type that implements the associated cmdlet - /// - public abstract Type TypeImplementingCmdlet { get; } - } - - - /// - /// Provides additional functionality for CIM activity implementations. - /// - public class CimActivityImplementationContext : ActivityImplementationContext - { - /// - /// Create an instance of the CIM activity implementation class - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public CimActivityImplementationContext( - ActivityImplementationContext activityImplementationContext, - string computerName, - PSCredential credential, - string certificateThumbprint, - AuthenticationMechanism? authenticationMechanism, - bool useSsl, - uint port, - PSSessionOption sessionOption, - CimSession session, - CimSessionOptions cimSessionOptions, - string moduleDefinition, - Uri resourceUri) - { - if (activityImplementationContext == null) - { - throw new ArgumentNullException("activityImplementationContext"); - } - this.PowerShellInstance = activityImplementationContext.PowerShellInstance; - ResourceUri = resourceUri; - ComputerName = computerName; - PSCredential = credential; - PSCertificateThumbprint = certificateThumbprint; - PSAuthentication = authenticationMechanism; - PSUseSsl = useSsl; - PSPort = port; - PSSessionOption = sessionOption; - Session = session; - SessionOptions = cimSessionOptions; - if (moduleDefinition != null) - { - // Creating a script block forces the string into the compiled script cache so we - // don't need to reparse it at execution time. Locking the static _moduleDefinition is not - // required since the operation is idempotent. - _moduleScriptBlock = ScriptBlock.Create(moduleDefinition); - _moduleDefinition = moduleDefinition; - } - } - - /// - /// Gets the scriptblock that implements this activity's command - /// - public ScriptBlock ModuleScriptBlock - { - get { return _moduleScriptBlock; } - } - private static ScriptBlock _moduleScriptBlock; - - /// - /// Defines the resource URI used by the CIM cmdlet. - /// - public Uri ResourceUri { get; set; } - - /// - /// The session specified for this activity - /// - public CimSession Session { get; set; } - - /// - /// The session options used to create the session for this activity... - /// - public CimSessionOptions SessionOptions { get; set; } - - /// - /// The name of the computer this activity targets - /// - public string ComputerName { get; set; } - - /// - /// Base64 encoded string defining this module... - /// - public string ModuleDefinition { get { return _moduleDefinition; } } - string _moduleDefinition; - - /// - /// Return the session to the session manager - /// - public override void CleanUp() - { - if (Session != null && !string.IsNullOrEmpty(ComputerName)) - { - CimConnectionManager.GetGlobalCimConnectionManager().ReleaseSession(ComputerName, Session); - Session = null; - ComputerName = null; - } - } - } - - - /// - /// Class to manage reuse of CIM connections. - /// - internal class CimConnectionManager - { - private readonly System.Timers.Timer _cleanupTimer = new System.Timers.Timer(); - private bool firstTime = true; - internal CimConnectionManager() - { - _cleanupTimer.Elapsed += HandleCleanupTimerElapsed; - _cleanupTimer.AutoReset = true; - _cleanupTimer.Interval = 20*1000; - _cleanupTimer.Start(); - } - - private void HandleCleanupTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) - { - if (firstTime) - { - // this is to enable a start delay of 20 seconds - firstTime = false; - return; - } - - lock (SyncRoot) - { - List computersToRemove = new List(); - foreach (var pair in availableSessions) - { - List sel = pair.Value; - if (sel.Count == 0) - { - computersToRemove.Add(pair.Key); - } - else - { - for (int i = sel.Count - 1; i >= 0; i--) - { - // If this connection in use, then skip it - if (sel[i].GetReferenceCount > 0) - continue; - - if (--sel[i].IterationsRemaining <= 0) - { - sel[i].Session.Close(); - sel.RemoveAt(i); - } - } - } - } - } - } - - // Number of scan cycles left until this session entry will be removed. - internal const int MaxIterations = 6; - - internal const int MaxCimSessionsUpperLimit = 500; - internal const int MaxCimSessionsLowerLimit = 1; - - Dictionary> availableSessions = new Dictionary>(); - - private class SessionEntry - { - public int IterationsRemaining = MaxIterations; - public CimSessionOptions SessionOptions; - public CimSession Session; - - public void AddReference() - { - Interlocked.Add(ref _numberOfUses, 1); - } - - public void RemoveReference() - { - Interlocked.Decrement(ref _numberOfUses); - } - int _numberOfUses; - - public int GetReferenceCount - { - get { return _numberOfUses; } - } - - public PSCredential Credential - { - get { return _credential; } - } - private PSCredential _credential; - - public bool UseSsl - { - get - { - return _useSsl; - } - } - private bool _useSsl; - - public uint Port - { - get - { - return _port; - } - } - private uint _port; - - - public PSSessionOption PSSessionOption - { - get - { - return _psSessionOption; - } - } - private PSSessionOption _psSessionOption; - - public string CertificateThumbprint - { - get - { - return _certificateThumbprint; - } - } - private string _certificateThumbprint; - - public AuthenticationMechanism AuthenticationMechanism - { - get - { - return _authenticationMechanism; - } - } - private AuthenticationMechanism _authenticationMechanism; - - public SessionEntry(string computerName, PSCredential credential, string certificateThumbprint, AuthenticationMechanism authenticationMechanism, CimSessionOptions sessionOptions, bool useSsl, uint port, PSSessionOption pssessionOption) - { - SessionOptions = sessionOptions; - _credential = credential; - _certificateThumbprint = certificateThumbprint; - _authenticationMechanism = authenticationMechanism; - _useSsl = useSsl; - _port = port; - _psSessionOption = pssessionOption; - Session = CimSession.Create(computerName, sessionOptions); - } - } - - object SyncRoot = new object(); - - /// - /// Get a CIM session for the target computer - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal CimSession GetSession(string computerName, PSCredential credential, string certificateThumbprint, AuthenticationMechanism authenticationMechanism, CimSessionOptions sessionOptions, bool useSsl, uint port, PSSessionOption pssessionOption) - { - System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(computerName), "ComputerName is null in GetSession. GetSession should not be called in this case."); - lock (SyncRoot) - { - SessionEntry newSessionEntry; - - if (availableSessions.ContainsKey(computerName)) - { - - List sel = availableSessions[computerName]; - if (sel.Count > 0) - { - for (int i = 0; i < sel.Count; i++) - { - SessionEntry se = sel[i]; - - // No session options specified or the object matches exactly... - if ((se.SessionOptions == null && sessionOptions == null) || CompareSessionOptions(se, sessionOptions, credential, certificateThumbprint, authenticationMechanism, useSsl, port, pssessionOption)) - { - // Up the number of references to this session object... - se.AddReference(); - return se.Session; - } - } - } - } - - - // Allocate a new session entry for this computer - - newSessionEntry = new SessionEntry(computerName, credential, certificateThumbprint, authenticationMechanism, sessionOptions, useSsl, port, pssessionOption); - newSessionEntry.IterationsRemaining = MaxIterations; - newSessionEntry.AddReference(); - if (! availableSessions.ContainsKey(computerName)) - { - availableSessions.Add(computerName, new List()); - } - - availableSessions[computerName].Add(newSessionEntry); - - // Return the session object - return newSessionEntry.Session; - } - } - - - - /// - /// Return a session to the session table. - /// - /// - /// - internal void ReleaseSession(string computerName, CimSession session) - { - System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(computerName), "ComputerName is null in ReleaseSession. ReleaseSession should not be called in this case."); - lock (SyncRoot) - { - if (availableSessions.ContainsKey(computerName)) - { - foreach (var se in availableSessions[computerName]) - { - if (se.Session == session) - { - se.RemoveReference(); - } - } - } - } - } - - private static bool CompareSessionOptions(SessionEntry sessionEntry, CimSessionOptions options2, PSCredential credential2, string certificateThumbprint, AuthenticationMechanism authenticationMechanism, bool useSsl, uint port, PSSessionOption pssessionOption) - { - if (!sessionEntry.SessionOptions.Timeout.Equals(options2.Timeout)) - return false; - - if (!string.Equals(sessionEntry.SessionOptions.Culture.ToString(), options2.Culture.ToString(), StringComparison.OrdinalIgnoreCase)) - return false; - - if (!string.Equals(sessionEntry.SessionOptions.UICulture.ToString(), options2.UICulture.ToString(), StringComparison.OrdinalIgnoreCase)) - return false; - - if (!string.Equals(sessionEntry.CertificateThumbprint, certificateThumbprint, StringComparison.OrdinalIgnoreCase)) - return false; - - if (sessionEntry.AuthenticationMechanism != authenticationMechanism) - return false; - - if (!Workflow.WorkflowUtils.CompareCredential(sessionEntry.Credential, credential2)) - return false; - - if (sessionEntry.UseSsl != useSsl) - return false; - - if (sessionEntry.Port != port) - return false; - - - // check PSSessionOption if present - if (pssessionOption == null ^ sessionEntry.PSSessionOption == null) - { - return false; - } - - if (pssessionOption != null && sessionEntry.PSSessionOption != null) - { - if (sessionEntry.PSSessionOption.ProxyAccessType != pssessionOption.ProxyAccessType) - return false; - - if (sessionEntry.PSSessionOption.ProxyAuthentication != pssessionOption.ProxyAuthentication) - return false; - - if (!Workflow.WorkflowUtils.CompareCredential(sessionEntry.PSSessionOption.ProxyCredential, pssessionOption.ProxyCredential)) - return false; - - if (sessionEntry.PSSessionOption.SkipCACheck != pssessionOption.SkipCACheck) - return false; - - if (sessionEntry.PSSessionOption.SkipCNCheck != pssessionOption.SkipCNCheck) - return false; - - if (sessionEntry.PSSessionOption.SkipRevocationCheck != pssessionOption.SkipRevocationCheck) - return false; - - if (sessionEntry.PSSessionOption.NoEncryption != pssessionOption.NoEncryption) - return false; - - if (sessionEntry.PSSessionOption.UseUTF16 != pssessionOption.UseUTF16) - return false; - } - - return true; - } - - /// - /// Gets the global instance of the CIM session manager - /// - /// - public static CimConnectionManager GetGlobalCimConnectionManager() - { - lock (gcmLock) - { - if (_globalConnectionManagerInstance == null) - { - _globalConnectionManagerInstance = new CimConnectionManager(); - } - return _globalConnectionManagerInstance; - } - } - static CimConnectionManager _globalConnectionManagerInstance; - static object gcmLock = new object(); - } -} - diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSWorkflowHost.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSWorkflowHost.cs deleted file mode 100644 index bc8da6838a8..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PSWorkflowHost.cs +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ -using System; -using System.Management.Automation.Runspaces; -using System.Reflection; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using Microsoft.PowerShell.Workflow; - -namespace Microsoft.PowerShell.Activities -{ - - #region PSWorkflowHost - - /// - /// Interface that defines the PowerShell workflow host - /// Workflow host defines the set of services that are - /// made available to an activity - /// - public abstract class PSWorkflowHost - { - /// - /// The activity host manager to use for processing - /// activities - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSActivityHostController PSActivityHostController - { - get - { - throw new NotImplementedException(); - } - } - - /// - /// The provider to be used for obtaining runspaces - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual RunspaceProvider RemoteRunspaceProvider - { - get - { - throw new NotImplementedException(); - } - } - - /// - /// The provider used to obtain local in-proc - /// runspaces - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual RunspaceProvider LocalRunspaceProvider - { - get { throw new NotImplementedException(); } - } - - /// - /// The provider which will supply an unbounded number of - /// runspaces - to be used in PowerShell value - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual RunspaceProvider UnboundedLocalRunspaceProvider - { - get { throw new NotImplementedException(); } - } - } - - #endregion PSWorkflowHost - - - #region RunspaceProvider - - /// - /// Class that provides a runspace with the specified options - /// and constraints - /// - public abstract class RunspaceProvider - { - /// - /// Begin for obtaining a runspace for the specified ConnectionInfo - /// - /// connection info to be used for remote connections - /// number of times to retry - /// optional user defined callback - /// optional user specified state - /// time in milliseconds before the next retry has to be attempted - /// - /// async result - public virtual IAsyncResult BeginGetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval, AsyncCallback callback, object state) - { - throw new NotImplementedException(); - } - - /// - /// End for obtaining a runspace for the specified connection info - /// - /// async result to end on - /// - /// remote runspace to invoke commands on - public virtual Runspace EndGetRunspace(IAsyncResult asyncResult) - { - throw new NotImplementedException(); - } - - /// - /// Get runspace for the specified connection info to be - /// used for running commands - /// - /// connection info to use - /// retry count - /// retry interval in ms - /// remote runspace to use - public virtual Runspace GetRunspace(WSManConnectionInfo connectionInfo, uint retryCount, uint retryInterval) - { - throw new NotImplementedException(); - } - - /// - /// Release the runspace once the activity is finished using the same - /// - /// runspace to release - public virtual void ReleaseRunspace(Runspace runspace) - { - throw new NotImplementedException(); - } - - /// - /// Callback to indicate that this runspace been initiated with - /// a pipeline and can be disconnected - /// - /// runspace that needs to be marked as - /// ready for disconnect - public virtual void ReadyForDisconnect(Runspace runspace) - { - throw new NotImplementedException(); - } - - /// - /// Request a cleanup to the destination specified in the - /// connection info. This means no runspaces will be held - /// to the specified connection info. - /// - /// connection info to which - /// cleanup is desired - ///callback to invoke - /// caller specified state - public virtual void RequestCleanup(WSManConnectionInfo connectionInfo, WaitCallback callback, object state) - { - throw new NotImplementedException(); - } - - /// - /// Checks to see if the provider intentionally disconnected a runspace - /// or it went into disconnected state due to network issues - /// - /// runspace that needs to be checked - /// true - when intentionally disconnected - /// false - disconnected due to network issues - public virtual bool IsDisconnectedByRunspaceProvider(Runspace runspace) - { - throw new NotImplementedException(); - } - } - - #endregion RunspaceProvider - - - #region DefaultWorkflowHost - - /// - /// Default workflow host implementation - /// - internal class DefaultWorkflowHost : PSWorkflowHost - { - private readonly PSActivityHostController _activityHostController; - private readonly RunspaceProvider _runspaceProvider; - private static readonly DefaultWorkflowHost _instance = new DefaultWorkflowHost(); - private readonly RunspaceProvider _localRunspaceProvider; - - private DefaultWorkflowHost() - { - const string applicationPrivateData = @" - - - -"; - var runtime = PSWorkflowRuntime.Instance; - runtime.Configuration.Populate(applicationPrivateData, "Microsoft.PowerShell.Workflow"); - _activityHostController = runtime.PSActivityHostController; - _runspaceProvider = runtime.RemoteRunspaceProvider; - _localRunspaceProvider = runtime.LocalRunspaceProvider; - } - - /// - /// The activity host manager to use for processing - /// activities - /// - public override PSActivityHostController PSActivityHostController - { - get - { - return _activityHostController; - } - } - - /// - /// The provider to be used for obtaining runspaces - /// - public override RunspaceProvider RemoteRunspaceProvider - { - get { return _runspaceProvider; } - } - - /// - /// - /// - public override RunspaceProvider LocalRunspaceProvider - { - get - { - return _localRunspaceProvider; - } - } - - /// - /// return the singleton instance - /// - internal static DefaultWorkflowHost Instance - { - get { return _instance; } - } - - internal void ResetLocalRunspaceProvider() - { - MethodInfo methodInfo = _localRunspaceProvider.GetType().GetMethod("Reset", BindingFlags.NonPublic | BindingFlags.Instance); - methodInfo.Invoke(_localRunspaceProvider, new object[]{}); - } - } - - #endregion DefaultWorkflowHost - -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PowerShellWorkflowHost.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PowerShellWorkflowHost.cs deleted file mode 100644 index b3ebd1f475f..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/PowerShellWorkflowHost.cs +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2010 Microsoft Corporation. All rights reserved - */ - -using Microsoft.PowerShell.Activities; -using System.Management.Automation.PerformanceData; -using System.Management.Automation.Tracing; -using System.Threading; -using System; -using System.Management.Automation; -using System.Globalization; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// PowerShell Workflow host runtime. - /// - public class PSWorkflowRuntime : PSWorkflowHost, IDisposable - { - private static PSWorkflowRuntime powerShellWorkflowHostInstance; - private static readonly object syncLock = new object(); - private RunspaceProvider _runspaceProvider; - private RunspaceProvider _localRunspaceProvider; - private RunspaceProvider _unboundedLocalRunspaceProvider; - - private static readonly Tracer _tracer = new Tracer(); - private static readonly PSPerfCountersMgr _psPerfCountersMgrInst = PSPerfCountersMgr.Instance; - private readonly PSWorkflowConfigurationProvider _configuration; - private readonly object _syncObject = new object(); - - private PSWorkflowJobManager jobManager; - private PSActivityHostController activityHostController; - - private bool _isDisposed; - - /// - /// Default constructor - /// - public PSWorkflowRuntime() - { - _configuration = new PSWorkflowConfigurationProvider(); - _configuration.Runtime = this; - PSCounterSetRegistrar registrar = - new PSCounterSetRegistrar( - PSWorkflowPerformanceCounterSetInfo.ProviderId, - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterSetInfo.CounterSetType, - PSWorkflowPerformanceCounterSetInfo.CounterInfoArray); - _psPerfCountersMgrInst.AddCounterSetInstance(registrar); - - // Enable caching module paths appdomain-wide. - System.Management.Automation.PSModuleInfo.UseAppDomainLevelModuleCache = true; - } - - /// - /// Constructs runtime based on configuration - /// - /// - public PSWorkflowRuntime(PSWorkflowConfigurationProvider configuration) - { - if (configuration == null) - throw new ArgumentNullException("configuration"); - - // Only allow FullLanguage or ConstraintLanguage or it can be null in that case system wide default will take an affect - PSLanguageMode? langMode = configuration.LanguageMode; - if (langMode != null && langMode.HasValue && (langMode.Value == PSLanguageMode.NoLanguage || langMode.Value == PSLanguageMode.RestrictedLanguage)) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.NotSupportedLanguageMode, langMode.Value.ToString())); - } - - _configuration = configuration; - _configuration.Runtime = this; - } - - /// - /// Dispose implementation. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose implementation. - /// - /// - private void Dispose(bool disposing) - { - if (_isDisposed || !disposing) - return; - - lock (_syncObject) - { - if (_isDisposed) - return; - - // Suspend all running jobs then dispose all runspace providers - if (jobManager != null) - jobManager.Dispose(); - jobManager = null; - - var remoteRunspaceProvider = _runspaceProvider as ConnectionManager; - if (remoteRunspaceProvider != null) - remoteRunspaceProvider.Dispose(); - _runspaceProvider = null; - - var localRunspaceProvider = _localRunspaceProvider as LocalRunspaceProvider; - if (localRunspaceProvider != null) - localRunspaceProvider.Dispose(); - _localRunspaceProvider = null; - - var unboundedLocalRunspaceProvider = _localRunspaceProvider as LocalRunspaceProvider; - if (unboundedLocalRunspaceProvider != null) - unboundedLocalRunspaceProvider.Dispose(); - _unboundedLocalRunspaceProvider = null; - - activityHostController = null; - - _isDisposed = true; - } - } - - private void AssertNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("PSWorkflowRuntime"); - } - } - - internal static PSWorkflowRuntime Instance - { - get - { - if (powerShellWorkflowHostInstance == null) - { - lock (syncLock) - { - if (powerShellWorkflowHostInstance == null) - { - powerShellWorkflowHostInstance = new PSWorkflowRuntime(); - } - } - } - return powerShellWorkflowHostInstance; - } - } - - internal Tracer Tracer - { - get - { - return _tracer; - } - } - - #region Test Helpers - - internal void SetWorkflowJobManager(PSWorkflowJobManager wfJobManager) - { - lock (syncLock) - { - jobManager = wfJobManager; - } - } - - #endregion Test Helpers - - /// - /// The runtime configuration. - /// - public virtual PSWorkflowConfigurationProvider Configuration - { - get - { - return _configuration; - } - } - - /// - /// JobManager - /// - public virtual PSWorkflowJobManager JobManager - { - get - { - if (jobManager != null) - return jobManager; - - lock (syncLock) - { - if (jobManager != null) - return jobManager; - - AssertNotDisposed(); - jobManager = new PSWorkflowJobManager(this, _configuration.MaxRunningWorkflows); - } - return jobManager; - } - } - - - /// - /// PSActivityHostController (PSWorkflowHost) - /// - public override PSActivityHostController PSActivityHostController - { - get - { - if (activityHostController != null) - return activityHostController; - - lock (syncLock) - { - if (activityHostController != null) - return activityHostController; - - AssertNotDisposed(); - activityHostController = _configuration.CreatePSActivityHostController(); - } - return activityHostController; - } - } - - /// - /// RemoteRunspaceProvider (PSWorkflowHost) - /// - public override RunspaceProvider RemoteRunspaceProvider - { - get - { - if (_runspaceProvider == null) - { - lock (_syncObject) - { - if (_runspaceProvider == null) - { - AssertNotDisposed(); - _runspaceProvider = _configuration.CreateRemoteRunspaceProvider(); - } - } - } - return _runspaceProvider; - } - } - - /// - /// LocalRunspaceProvider (PSWorkflowHost) - /// - public override RunspaceProvider LocalRunspaceProvider - { - get - { - if (_localRunspaceProvider == null) - { - lock (_syncObject) - { - if (_localRunspaceProvider == null) - { - AssertNotDisposed(); - _localRunspaceProvider = _configuration.CreateLocalRunspaceProvider(false); - } - } - } - - return _localRunspaceProvider; - } - } - - /// - /// The provider which will supply an unbounded number of - /// runspaces - to be used in PowerShell value - /// - public override RunspaceProvider UnboundedLocalRunspaceProvider - { - get - { - if (_unboundedLocalRunspaceProvider == null) - { - lock(_syncObject) - { - if (_unboundedLocalRunspaceProvider == null) - { - AssertNotDisposed(); - _unboundedLocalRunspaceProvider = _configuration.CreateLocalRunspaceProvider(true); - } - } - } - - return _unboundedLocalRunspaceProvider; - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/RuntimeBase.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/RuntimeBase.cs deleted file mode 100644 index 8bfe23839a4..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/RuntimeBase.cs +++ /dev/null @@ -1,834 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.Activities; -using System.Activities.Validation; -using System.Runtime.DurableInstancing; -using System.Collections.Generic; -using System.Management.Automation; -using System.Collections.ObjectModel; -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using Microsoft.PowerShell.Activities; -using System.Diagnostics; -using System.Reflection; -using System.Globalization; -using System.Runtime.Serialization; -using System.Activities.Persistence; -using System.Activities.Hosting; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// WorkflowStoreComponent - /// - [Flags] - public enum WorkflowStoreComponents - { - /// - /// Streams - /// - Streams = 1, - - /// - /// Metadata - /// - Metadata = 2, - - /// - /// Definition - /// - Definition = 4, - - /// - /// Timers - /// - Timer = 8, - - /// - /// JobState - /// - JobState = 16, - - /// - /// TerminatingError - /// - TerminatingError = 32, - - /// - /// ActivityState, like remote activity execution state, etc. - /// - ActivityState = 64, - } - - /// - /// ActivityRunMode - /// - public enum ActivityRunMode - { - /// - /// InProcess - /// - InProcess = 0, - - /// - /// OutOfProcess - /// - OutOfProcess = 1, - } - - /// - /// WorkflowInstanceStore - /// - public abstract class PSWorkflowInstanceStore - { - /// - /// PSWorkflowInstanceStore - /// - /// - protected PSWorkflowInstanceStore(PSWorkflowInstance workflowInstance) - { - if (workflowInstance == null) - throw new ArgumentNullException("workflowInstance"); - - PSWorkflowInstance = workflowInstance; - } - - /// - /// PSWorkflowInstance - /// - public PSWorkflowInstance PSWorkflowInstance - { - get; - private set; - } - - /// - /// CreatePersistenceIOParticipant - /// - /// - public abstract PersistenceIOParticipant CreatePersistenceIOParticipant(); - - /// - /// CreateInstanceStore - /// - /// - public abstract InstanceStore CreateInstanceStore(); - - #region Save Methods - - /// - /// Save - /// - /// - public void Save(WorkflowStoreComponents components) - { - this.Save(components, null); - } - - internal void Save(WorkflowStoreComponents components, Dictionary WorkflowContext) - { - Collection componentsToSave = new Collection(); - - if ((components & WorkflowStoreComponents.JobState) == WorkflowStoreComponents.JobState) - { - componentsToSave.Add(PSWorkflowInstance.State); - } - - if (WorkflowContext != null) - { - componentsToSave.Add(WorkflowContext); - } - - if (((components & WorkflowStoreComponents.Definition) == WorkflowStoreComponents.Definition) && - (PSWorkflowInstance.PSWorkflowDefinition != null)) - { - componentsToSave.Add(PSWorkflowInstance.PSWorkflowDefinition); - } - - if (((components & WorkflowStoreComponents.TerminatingError) == WorkflowStoreComponents.TerminatingError) && - (PSWorkflowInstance.Error != null)) - { - if (!WorkflowJobSourceAdapter.GetInstance().IsShutdownInProgress) - { - if (PSWorkflowInstance.Error.GetType() != typeof(RemoteException)) - { - componentsToSave.Add(PSWorkflowInstance.Error); - } - } - } - - if (((components & WorkflowStoreComponents.Metadata) == WorkflowStoreComponents.Metadata) && - (PSWorkflowInstance.PSWorkflowContext != null)) - { - componentsToSave.Add(PSWorkflowInstance.PSWorkflowContext); - } - - if (((components & WorkflowStoreComponents.Streams) == WorkflowStoreComponents.Streams) && - (PSWorkflowInstance.Streams != null)) - { - componentsToSave.Add(PSWorkflowInstance.Streams); - } - - if (((components & WorkflowStoreComponents.ActivityState) == WorkflowStoreComponents.ActivityState) && - (PSWorkflowInstance.RemoteActivityState != null)) - { - componentsToSave.Add(PSWorkflowInstance.RemoteActivityState); - } - - if (((components & WorkflowStoreComponents.Timer) == WorkflowStoreComponents.Timer) && - PSWorkflowInstance.Timer != null) - { - componentsToSave.Add(PSWorkflowInstance.Timer); - } - - DoSave(componentsToSave); - } - - /// - /// DoSave - /// - /// - protected abstract void DoSave(IEnumerable components); - - #endregion Save - - #region Load Methods - - /// - /// Load - /// - /// - public void Load(WorkflowStoreComponents components) - { - Collection componentsToLoad = new Collection(); - - if ((components & WorkflowStoreComponents.JobState) == WorkflowStoreComponents.JobState) - { - componentsToLoad.Add(typeof(JobState)); - PSWorkflowInstance.JobStateRetrieved = false; - } - - if ((components & WorkflowStoreComponents.Definition) == WorkflowStoreComponents.Definition) - { - componentsToLoad.Add(typeof(PSWorkflowDefinition)); - PSWorkflowInstance.PSWorkflowDefinition = null; - } - - if ((components & WorkflowStoreComponents.TerminatingError) == WorkflowStoreComponents.TerminatingError) - { - componentsToLoad.Add(typeof(Exception)); - PSWorkflowInstance.Error = null; - } - - if ((components & WorkflowStoreComponents.Metadata) == WorkflowStoreComponents.Metadata) - { - componentsToLoad.Add(typeof(PSWorkflowContext)); - PSWorkflowInstance.PSWorkflowContext = null; - } - - if ((components & WorkflowStoreComponents.Streams) == WorkflowStoreComponents.Streams) - { - componentsToLoad.Add(typeof(PowerShellStreams)); - PSWorkflowInstance.Streams = null; - } - - if ((components & WorkflowStoreComponents.ActivityState) == WorkflowStoreComponents.ActivityState) - { - componentsToLoad.Add(typeof(PSWorkflowRemoteActivityState)); - PSWorkflowInstance.RemoteActivityState = null; - } - - if ((components & WorkflowStoreComponents.Timer) == WorkflowStoreComponents.Timer) - { - componentsToLoad.Add(typeof(PSWorkflowTimer)); - PSWorkflowInstance.Timer = null; - } - - IEnumerable loadedComponents = DoLoad(componentsToLoad); - - foreach (object loadedComponent in loadedComponents) - { - Type componentType = loadedComponent.GetType(); - - if (componentType == typeof(JobState)) - { - PSWorkflowInstance.State = (JobState)loadedComponent; - PSWorkflowInstance.JobStateRetrieved = true; - } - else if (componentType == typeof(PSWorkflowDefinition)) - { - PSWorkflowInstance.PSWorkflowDefinition = (PSWorkflowDefinition)loadedComponent; - } - else if (loadedComponent is Exception) - { - PSWorkflowInstance.Error = (Exception)loadedComponent; - } - else if (componentType == typeof(PSWorkflowContext)) - { - PSWorkflowInstance.PSWorkflowContext = (PSWorkflowContext)loadedComponent; - } - else if (componentType == typeof(PowerShellStreams)) - { - PSWorkflowInstance.Streams = (PowerShellStreams)loadedComponent; - } - else if (componentType == typeof(PSWorkflowTimer)) - { - PSWorkflowInstance.Timer = (PSWorkflowTimer)loadedComponent; - } - else if (componentType == typeof(PSWorkflowRemoteActivityState)) - { - PSWorkflowInstance.RemoteActivityState = (PSWorkflowRemoteActivityState)loadedComponent; - } - } - } - - internal Dictionary LoadWorkflowContext() - { - Dictionary workflowContext = null; - - Collection componentsToLoad = new Collection(); - componentsToLoad.Add(typeof(Dictionary)); - - IEnumerable loadedComponents = DoLoad(componentsToLoad); - - foreach (object loadedComponent in loadedComponents) - { - Type componentType = loadedComponent.GetType(); - - if (componentType == typeof(Dictionary)) - { - workflowContext = (Dictionary)loadedComponent; - } - } - - return workflowContext; - } - - /// - /// DoLoad - /// - /// - protected abstract IEnumerable DoLoad(IEnumerable componentTypes); - - #endregion Load Methods - - #region Delete Methods - - /// - /// Delete - /// - public void Delete() - { - DoDelete(); - } - - /// - /// DoDelete - /// - protected abstract void DoDelete(); - - #endregion Delete Methods - } - - - /// - /// WorkflowInstance - /// - public abstract class PSWorkflowInstance : IDisposable - { - #region Private Members - - /// - /// _syncLock - /// - private object _syncLock = new object(); - - #endregion Private Members - - #region Protected Members - - /// - /// _disposed - /// - protected bool Disposed - { - set; - get; - } - - /// - /// Synchronization object available to derived classes. - /// - protected object SyncLock - { - get - { - return _syncLock; - } - } - - - /// - /// DoStopInstance - /// - protected virtual void DoStopInstance() - { - throw new NotImplementedException(); - } - - /// - /// DoAbortInstance - /// - /// Reason for aborting workflow. - protected virtual void DoAbortInstance(string reason) - { - throw new NotImplementedException(); - } - - /// - /// DoTerminateInstance - /// - /// Reason message for termination - protected virtual void DoTerminateInstance(string reason) - { - throw new NotImplementedException(); - } - - /// - /// DoTerminateInstance - /// - /// Reason message for termination - /// Suppress error for termination - protected virtual void DoTerminateInstance(string reason, bool suppressError) - { - throw new NotImplementedException(); - } - - /// - /// DoResumeInstance - /// - protected virtual void DoResumeInstance(string label) - { - throw new NotImplementedException(); - } - - /// - /// DoSuspendInstance - /// - /// - protected virtual void DoSuspendInstance(bool notStarted) - { - throw new NotImplementedException(); - } - - /// - /// DoExecuteInstance - /// - protected virtual void DoExecuteInstance() - { - throw new NotImplementedException(); - } - - /// - /// DoResumeBookmark - /// - /// - /// - protected virtual void DoResumeBookmark(Bookmark bookmark, object state) - { - throw new NotImplementedException(); - } - - /// - /// Loads the xaml to create an executable activity. - /// - protected virtual void DoCreateInstance() - { - throw new NotImplementedException(); - } - - /// - /// Remove - /// - protected virtual void DoRemoveInstance() - { - throw new NotImplementedException(); - } - - - /// - /// DoPersistInstance - /// - protected virtual void DoPersistInstance() - { - throw new NotImplementedException(); - } - - /// - /// DoGetPersistableIdleAction - /// - /// - /// - /// - protected virtual PSPersistableIdleAction DoGetPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest) - { - throw new NotImplementedException(); - } - - /// - /// Dispose - /// - protected virtual void Dispose(bool disposing) - { - if (!disposing || Disposed) - return; - - lock (SyncLock) - { - if (Disposed) - return; - - Disposed = true; - - this.OnCompleted = null; - this.OnFaulted = null; - this.OnStopped = null; - this.OnAborted = null; - this.OnSuspended = null; - this.OnIdle = null; - this.OnPersistableIdleAction = null; - this.OnUnloaded = null; - } - } - #endregion Protected Members - - #region Internal Members - internal PSWorkflowRuntime Runtime { get; set; } - - internal void CreateInstance() { this.DoCreateInstance(); } - internal void ExecuteInstance() { this.DoExecuteInstance(); } - internal void SuspendInstance(bool notStarted) { this.DoSuspendInstance(notStarted); } - internal void RemoveInstance() { this.DoRemoveInstance(); } - internal void ResumeInstance(string label) { this.DoResumeInstance(label); } - internal void ResumeBookmark(Bookmark bookmark, object state) { this.DoResumeBookmark(bookmark, state); } - internal void StopInstance() { this.DoStopInstance(); } - internal void AbortInstance(string reason) { this.DoAbortInstance(reason); } - internal void TerminateInstance(string reason, bool suppressError) { this.DoTerminateInstance(reason, suppressError); } - internal void PersistInstance() { this.DoPersistInstance(); } - internal PSPersistableIdleAction GetPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest) { return this.DoGetPersistableIdleAction(bookmarks, externalSuspendRequest); } - - /// - /// Gets the Guid of workflow instance. - /// - internal virtual Guid Id - { - get { return this.InstanceId.Guid; } - } - - /// - /// JobStateRetrieved - /// - internal bool JobStateRetrieved { get; set; } - - internal bool ForceDisableStartOrEndPersistence { get; set; } - - /// - /// CheckForTerminalAction - /// - internal virtual void CheckForTerminalAction() - { - throw new NotImplementedException(); - } - - /// - /// Load instance for resuming the workflow - /// - internal virtual void DoLoadInstanceForReactivation() - { - throw new NotImplementedException(); - } - - /// - /// PerformTaskAtTerminalState - /// - internal virtual void PerformTaskAtTerminalState() - { - throw new NotImplementedException(); - } - - /// - /// On completed handler. - /// - internal Action OnCompletedDelegate - { - set { this.OnCompleted = value; } - } - - /// - /// On faulted handler. - /// - internal Action OnFaultedDelegate - { - set { this.OnFaulted = value; } - } - - /// - /// On stopped handler. - /// - internal Action OnStoppedDelegate - { - set { this.OnStopped = value; } - } - - /// - /// On aborted handler. - /// - internal Action OnAbortedDelegate - { - set { this.OnAborted = value; } - } - - /// - /// On suspended handler. - /// - internal Action OnSuspendedDelegate - { - set { this.OnSuspended = value; } - } - - /// - /// On idle handler. - /// - internal Action, object> OnIdleDelegate - { - set { this.OnIdle = value; } - } - - - /// - /// On persistable idle action handler. - /// - internal Func, bool, object, PSPersistableIdleAction> OnPersistableIdleActionDelegate - { - set { this.OnPersistableIdleAction = value; } - } - - /// - /// On unloaded handler. - /// - internal Action OnUnloadedDelegate - { - set { this.OnUnloaded = value; } - } - - /// - /// Validate label - /// - /// - internal virtual void ValidateIfLabelExists(string label) - { - throw new NotImplementedException(); - } - - internal virtual bool SaveStreamsIfNecessary() - { - return false; - } - - #endregion Internal Members - - - #region protected delegate members - - /// - /// On completed handler. - /// - protected Action OnCompleted { get; private set; } - - /// - /// On faulted handler. - /// - protected Action OnFaulted { get; private set; } - - /// - /// On stopped handler. - /// - protected Action OnStopped { get; private set; } - - /// - /// On aborted handler. - /// - protected Action OnAborted { get; private set; } - - /// - /// On suspended handler. - /// - protected Action OnSuspended { get; private set; } - - /// - /// On idle handler. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "????")] - protected Action, object> OnIdle { get; private set; } - - /// - /// On persistable idle action handler. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "????")] - protected Func, bool, object, PSPersistableIdleAction> OnPersistableIdleAction { get; private set; } - - /// - /// On unloaded handler. - /// - protected Action OnUnloaded { get; private set; } - - #endregion protected delegate members - - - #region Public Members - - /// - /// PSWorkflowJob - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowJob PSWorkflowJob - { - get { throw new NotImplementedException();} - protected internal set { throw new NotImplementedException(); } - } - - /// - /// Gets the Guid of workflow instance. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowId InstanceId - { - get { throw new NotImplementedException(); } - } - - /// - /// Gets the workflow job creation context. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual Dictionary CreationContext - { - get { throw new NotImplementedException(); } - } - - /// - /// State - /// - public virtual JobState State - { - get; - set; - } - - /// - /// InstanceStore - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowInstanceStore InstanceStore - { - get{ throw new NotImplementedException(); } - } - - /// - /// Gets the definition of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowDefinition PSWorkflowDefinition - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets the streams of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PowerShellStreams Streams - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets/Sets the RemoteActivityState of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowRemoteActivityState RemoteActivityState - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - /// - /// Gets the streams of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Error")] - public virtual Exception Error - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets the timers of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowTimer Timer - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets the metadatas of workflow. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - public virtual PSWorkflowContext PSWorkflowContext - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - /// - /// Gets the workflow debugger. - /// - [SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] - internal virtual PSWorkflowDebugger PSWorkflowDebugger - { - get { throw new NotImplementedException(); } - } - - /// - /// Dispose - /// - public void Dispose() - { - if (Disposed) - return; - - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose the streams to save memory - /// - public virtual void DisposeStreams() - { - - } - - #endregion Public Members - - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/TimeBasedCache.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/TimeBasedCache.cs deleted file mode 100644 index 620e286b99e..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/TimeBasedCache.cs +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2011 Microsoft Corporation. All rights reserved - */ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.ObjectModel; -using System.Timers; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// Bounded cache which will clear items when not used for a while - /// - /// type of item to use - internal class TimeBasedCache : IEnumerable, IDisposable - { - private readonly int _timeoutInSeconds; - private readonly object _timerServicingSyncObject = new object(); - private readonly Timer _validationTimer; - private int _timerFired; - private const int TimerFired = 1; - private const int TimerReset = 0; - private readonly ConcurrentDictionary> _cache = new ConcurrentDictionary>(); - - /// - /// - /// - internal ConcurrentDictionary> Cache - { - get { return _cache; } - } - - /// - /// The consumer of this class should hold a lock - /// on this object when servicing requests and adding - /// to this cache - /// - internal object TimerServicingSyncObject - { - get { return _timerServicingSyncObject; } - } - - internal TimeBasedCache(int timeoutInSeconds) - { - _timeoutInSeconds = timeoutInSeconds; - _validationTimer = new Timer - { - AutoReset = true, - Interval = _timeoutInSeconds*1000, - Enabled = false - }; - _validationTimer.Elapsed += ValidationTimerElapsed; - _validationTimer.Start(); - } - - private void ValidationTimerElapsed(object sender, ElapsedEventArgs e) - { - // ensure that the servicing thread is done before proceeding - if (_timerFired == TimerFired) return; - - lock(TimerServicingSyncObject) - { - if (_timerFired == TimerFired) return; - _timerFired = TimerFired; - Collection> toRemove = new Collection>(); - - foreach(Item item in _cache.Values) - { - if (item.Idle) - toRemove.Add(item); - else if (!item.Busy) - item.Idle = true; - } - - foreach(Item item in toRemove) - { - Item removedItem; - _cache.TryRemove(item.InstanceId, out removedItem); - IDisposable disposable = removedItem.Value as IDisposable; - if (disposable == null) continue; - - //dispose and force early GC - disposable.Dispose(); - disposable = null; - removedItem = null; - } - - _timerFired = TimerReset; - } - } - - /// - /// - /// - internal void Add(Item item) - { - item.Busy = true; - item.Idle = false; - _cache.TryAdd(item.InstanceId, item); - } - - /// - /// - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// - /// - /// - protected void Dispose(bool disposing) - { - if (disposing) - { - _validationTimer.Dispose(); - } - } - - internal class CacheEnumerator : IEnumerator - { - private readonly ConcurrentDictionary> _cache; - private readonly IEnumerator _dictionaryEnumerator; - private Item _currentItem; - - internal CacheEnumerator(ConcurrentDictionary> cache) - { - _cache = cache; - _dictionaryEnumerator = _cache.Keys.GetEnumerator(); - } - - /// - /// Advances the enumerator to the next element of the collection. - /// - /// - /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. - /// - /// The collection was modified after the enumerator was created. 2 - public bool MoveNext() - { - if (_dictionaryEnumerator.MoveNext()) - { - Guid key = (Guid)_dictionaryEnumerator.Current; - _currentItem = default(Item); - _cache.TryGetValue(key, out _currentItem); - - if (_currentItem != null) - return true; - } - - return false; - } - - /// - /// Sets the enumerator to its initial position, which is before the first element in the collection. - /// - /// The collection was modified after the enumerator was created. 2 - public void Reset() - { - _dictionaryEnumerator.Reset(); - } - - /// - /// Gets the current element in the collection. - /// - /// - /// The current element in the collection. - /// - /// The enumerator is positioned before the first element of the collection or after the last element.2 - public object Current - { - get { return _currentItem; } - } - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - /// 2 - public IEnumerator GetEnumerator() - { - return new CacheEnumerator(_cache); - } - } - - /// - /// one item that will be used in the cache - /// - /// type of item to use - internal class Item - { - private readonly Guid _instanceId; - private readonly T _value; - internal bool Busy { get; set; } - internal bool Idle { get; set; } - internal T Value - { - get { return _value; } - } - internal Guid InstanceId - { - get { return _instanceId; } - } - internal Item(T value, Guid instanceId) - { - _value = value; - _instanceId = instanceId; - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDebugger.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDebugger.cs deleted file mode 100644 index 3f5d8e51b78..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDebugger.cs +++ /dev/null @@ -1,1592 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Activities; -using System.Management.Automation; -using System.Management.Automation.Tracing; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Language; -using System.Management.Automation.Host; -using System.Globalization; -using System.Diagnostics.CodeAnalysis; -using System.Activities.Tracking; -using Microsoft.PowerShell.Activities; -using Microsoft.PowerShell.Commands; -using System.Collections.ObjectModel; -using System.Activities.Hosting; -using System.Xml; -using System.Linq; -using System.Management.Automation.Security; -using Dbg = System.Diagnostics.Debug; - -namespace Microsoft.PowerShell.Workflow -{ - #region PSWorkflowDebuggerMode - - /// - /// PSWorkflowDebuggerMode - /// - internal enum WorkflowDebugMode - { - /// - /// Debugger is off. - /// - Off = 0, - - /// - /// Debugger stops at breakpoints. - /// - StopAtBreakpoint, - - /// - /// Debugger stops at each Activity within a workflow function, - /// and will step over nested workflow functions. - /// - StepToNextActivity, - - /// - /// Debugger steps into nested workflow functions. - /// - StepIntoFunction, - - /// - /// Debugger steps out of nested workflow functions. - /// - StepOutOfFunction - } - - #endregion - - #region DebuggerActivityPosition - - internal sealed class ActivityPosition - { - #region Members - - private Tuple _activityPosition; - - #endregion - - #region Constructors - - private ActivityPosition() { } - - /// - /// Constructor - /// - /// - /// - /// - internal ActivityPosition( - string name, - int line, - int col) - { - _activityPosition = new Tuple( - (string.IsNullOrEmpty(name)) ? string.Empty : name, - line, - col); - } - - #endregion - - #region Properties - - /// - /// Workflow function name - /// - internal string Name - { - get { return _activityPosition.Item1; } - } - - /// - /// Script line position - /// - internal int Line - { - get { return _activityPosition.Item2; } - } - - /// - /// Script column position - /// - internal int Column - { - get { return _activityPosition.Item3; } - } - - #endregion - } - - #endregion - - /// - /// Workflow Debugger - /// - internal sealed class PSWorkflowDebugger : Debugger, IDisposable - { - #region Members - - private Debugger _parent; - private Dictionary _lineBreakPoints; - private Dictionary _variableBreakPoints; - private Dictionary _commandBreakpoints; - private Dictionary _initalParentBreakpoints; - private Dictionary _variables; - private Dictionary _wfMetaVariables; - private PSWorkflowInstance _wfInstance; - private WorkflowDebugMode _mode; - private string _scriptName; - private string _script; - private string[] _scriptLines; - private string _xamlDefinition; - private string _outerFnXamlDefinition; - private SortedSet _validWFLineNumbers; - private DebuggerStopEventArgs _debuggerStopEventArgs; - private Stack _nestedCallStack; - private IEqualityComparer _invocationComparer; - private int _breakFrame; - private Dictionary _funcToSourceMap; - private Dictionary> _funcToValidLineNumbersMap; - - private Runspace _runspace; - private System.Management.Automation.PowerShell _psDebuggerCommand; - private IAsyncResult _psAsyncResult; - private PSHost _host; - private PathInfo _parentPath; - - private const string DefaultWFPrompt = "PS>> "; - private const string WFDebugPrompt = "[WFDBG:{0}]: "; - private const string DebugContextName = "PSDebugContext"; - - #endregion - - #region Constructors - - private PSWorkflowDebugger() - { } - - /// - /// Constructor - /// - /// PSWorkflowInstance - internal PSWorkflowDebugger(PSWorkflowInstance wfInstance) - { - if (wfInstance == null) - { - throw new PSArgumentNullException("wfInstance"); - } - - _wfInstance = wfInstance; - _lineBreakPoints = new Dictionary(); - _variableBreakPoints = new Dictionary(); - _commandBreakpoints = new Dictionary(); - _initalParentBreakpoints = new Dictionary(); - _variables = new Dictionary(); - _wfMetaVariables = new Dictionary(); - _nestedCallStack = new Stack(); - _invocationComparer = new InvocationInfoComparer(); - _mode = WorkflowDebugMode.Off; - _funcToSourceMap = new Dictionary(); - _funcToValidLineNumbersMap = new Dictionary>(); - } - - #endregion - - #region Debugger Overrides - - /// - /// ProcessCommand - /// - /// PSCommand - /// - /// DebuggerCommandResults - public override DebuggerCommandResults ProcessCommand(PSCommand command, PSDataCollection output) - { - if (command == null) - { - throw new PSArgumentNullException("command"); - } - - if (output == null) - { - throw new PSArgumentNullException("output"); - } - - if (!DebuggerStopped) - { - throw new PSInvalidOperationException(Resources.ProcessDebugCommandNotInDebugStopMode); - } - - // First give debugger chance to handle command. - DebuggerCommandResults results = InternalProcessCommand(command, output); - if (results != null) { return results; } - - // Check to see if command modifies Workflow variables, which is not allowed. - ErrorRecord varChangedError; - if (CheckForWorkflowVariableChange(command, output, out varChangedError)) - { - Dbg.Assert(varChangedError != null, "Error record cannot be null."); - - // Command attempted to modify a workflow variable. - // Write error message and return. - PSCommand errorCommand = new PSCommand(); - errorCommand.AddCommand("Write-Error").AddParameter("ErrorRecord", varChangedError); - - if ((_host != null) && - !(_host.GetType().FullName.Equals("System.Management.Automation.Remoting.ServerRemoteHost", StringComparison.OrdinalIgnoreCase))) - { - errorCommand.AddCommand("Out-Default"); - } - - return RunPowerShellCommand(errorCommand, null, output, false); - } - - bool addToHistory = CheckAddToHistory(command); - - return RunPowerShellCommand(command, null, output, addToHistory); - } - - /// - /// StopProcessCommand - /// - public override void StopProcessCommand() - { - System.Management.Automation.PowerShell ps = _psDebuggerCommand; - if (ps != null) - { - ps.BeginStop(null, null); - } - } - - /// - /// SetDebuggerAction - /// - /// DebuggerResumeAction to set - public override void SetDebuggerAction(DebuggerResumeAction resumeAction) - { - switch (resumeAction) - { - case DebuggerResumeAction.Continue: - _mode = WorkflowDebugMode.StopAtBreakpoint; - break; - - case DebuggerResumeAction.StepOut: - _mode = WorkflowDebugMode.StepOutOfFunction; - break; - - case DebuggerResumeAction.StepOver: - _mode = WorkflowDebugMode.StepToNextActivity; - break; - - case DebuggerResumeAction.StepInto: - _mode = WorkflowDebugMode.StepIntoFunction; - break; - - case DebuggerResumeAction.Stop: - _mode = WorkflowDebugMode.Off; - break; - - default: - _mode = WorkflowDebugMode.StopAtBreakpoint; - break; - } - } - - /// - /// DebuggerStopEventArgs - /// - /// DebuggerStopEventArgs - public override DebuggerStopEventArgs GetDebuggerStopArgs() - { - return _debuggerStopEventArgs; - } - - /// - /// Sets the parent debugger and breakpoints. - /// - /// Parent debugger - /// List of breakpoints - /// Debugger mode - /// host - /// Current path - public override void SetParent( - Debugger parent, - IEnumerable breakPoints, - DebuggerResumeAction? mode, - PSHost host, - PathInfo path) - { - if (parent == null) { throw new PSArgumentNullException("parent"); } - - _parent = parent; - _host = host; - _parentPath = path; - - // Add breakpoints for this workflow. - if (breakPoints != null) - { - _lineBreakPoints.Clear(); - _variableBreakPoints.Clear(); - _commandBreakpoints.Clear(); - foreach (var bp in breakPoints) - { - // Add breakpoints to local collections. - AddBreakpoint(bp); - } - } - - if (mode != null) - { - SetDebuggerAction(mode.Value); - } - } - - /// - /// Sets the parent debugger, breakpoints and function source map. - /// - /// Parent debugger - /// List of breakpoints - /// Debugger mode - /// host - /// Current path - /// Function to source map - public override void SetParent( - Debugger parent, - IEnumerable breakPoints, - DebuggerResumeAction? mode, - PSHost host, - PathInfo path, - Dictionary functionSourceMap) - { - if (functionSourceMap == null) { throw new PSArgumentNullException("functionSourceMap"); } - - // Set parent info using original API. - SetParent(parent, breakPoints, mode, host, path); - - // Add WF function to source information. - foreach (var item in functionSourceMap) - { - if (!_funcToSourceMap.ContainsKey(item.Key)) - { - _funcToSourceMap.Add(item.Key, item.Value); - } - } - } - - /// - /// Returns IEnumerable of CallStackFrame objects. - /// - /// - public override IEnumerable GetCallStack() - { - Debugger parentDebugger = _parent; - if (parentDebugger != null) - { - // First return WF call stack items. - foreach (var invocationInfo in _nestedCallStack) - { - yield return new CallStackFrame(invocationInfo); - } - - // Then return parent script call stack items. - foreach (var frame in parentDebugger.GetCallStack()) - { - yield return frame; - } - } - } - - /// - /// Sets debugger stepping mode. - /// - /// True if stepping is to be enabled - public override void SetDebuggerStepMode(bool enabled) - { - if (enabled) - { - SetDebuggerAction(DebuggerResumeAction.StepInto); - } - else - { - SetDebuggerAction(DebuggerResumeAction.Continue); - } - } - - #endregion - - #region IDisposable - - /// - /// Dispose - /// - public void Dispose() - { - if (_runspace != null) - { - _runspace.Debugger.BreakpointUpdated -= HandleRunspaceBreakpointUpdated; - _runspace.Dispose(); - _runspace = null; - } - - _parent = null; - } - - #endregion - - #region Internal Methods - - /// - /// DebuggerCheck - /// - /// Defines activity execution position in XAML definition - internal void DebuggerCheck( - ActivityPosition activityPosition) - { - string funcName = activityPosition.Name; - - // - // PSWorkflowInstance.PSWorkflowDefinition may be null when debugger - // object is created. Lazily check and create Xaml related info when - // needed. - // - if (!CheckXamlInfo()) - { - return; - } - - // - // PSWorkflowInstance.PSWorkflowJob may be null when debugger - // object is created. Lazily check and create script info when - // needed. - // - if (!CheckWorkflowJob(funcName)) - { - return; - } - - // Update script information with current WF function. - UpdateScriptInfo(funcName); - - // Update call stack with current WF function. - if (!UpdateCallStack(activityPosition)) - { - return; - } - - // Let debugger update script and callstack information but - // if debugger is off or not subscribed-to then return here. - if (!IsDebuggerStopEventSubscribed() || - (_mode == WorkflowDebugMode.Off)) - { - return; - } - - SetWorkflowVariablesToRunspace(); - - DebuggerStopEventArgs args = EvaluateForStop(activityPosition); - if (args != null) - { - SetWorkflowDebuggerContextVariable(args); - - _debuggerStopEventArgs = new DebuggerStopEventArgs( - args.InvocationInfo, - new Collection(args.Breakpoints), - args.ResumeAction); - - try - { - // Blocking call. - RaiseDebuggerStopEvent(args); - } - finally - { - _debuggerStopEventArgs = null; - } - - // Update debugger mode based on resumeAction. - SetDebuggerAction(args.ResumeAction); - - if (args.ResumeAction == DebuggerResumeAction.Stop) - { - _nestedCallStack.Clear(); - - if (_wfInstance.PSWorkflowJob != null) - { - // Stop and remove job from job manager. - _wfInstance.PSWorkflowJob.StopJobAsync(true, - Resources.JobStoppedByDebugger, true); - } - } - } - - RemoveWorkflowVariablesFromRunspace(); - } - - /// - /// UpdateVariables - /// - /// - internal void UpdateVariables(IDictionary data) - { - if (!IsDebuggerStopEventSubscribed() || - (_mode == WorkflowDebugMode.Off)) - { - return; - } - - this._variables = new Dictionary(data); - } - - #endregion - - #region Private Methods - - private bool UpdateCallStack( - ActivityPosition activityPosition) - { - var invocationInfo = CreateInvocationInfo(activityPosition); - if (invocationInfo == null) { return false; } - if (_nestedCallStack.Contains(invocationInfo, _invocationComparer)) - { - // Unwind stack to this position and update invocation info. - // We can do this because workflows does not allow recursion calls - // and each workflow function name on the stack must be unique. - while ((_nestedCallStack.Count > 0) && - (_invocationComparer.Equals(_nestedCallStack.Peek(), invocationInfo) == false)) - { - _nestedCallStack.Pop(); - } - - Dbg.Assert(_nestedCallStack.Count > 0, "Should have unwound stack to known location."); - - // Replace invocationInfo item on stack. - _nestedCallStack.Pop(); - _nestedCallStack.Push(invocationInfo); - } - else - { - // Push invocationInfo item on top of stack. - _nestedCallStack.Push(invocationInfo); - } - - return true; - } - - private DebuggerStopEventArgs EvaluateForStop( - ActivityPosition activityPosition) - { - WorkflowDebugMode mode = _mode; - Breakpoint bp = null; - DebuggerResumeAction resumeAction = DebuggerResumeAction.Continue; - bool doStop = false; - - switch (mode) - { - case WorkflowDebugMode.StopAtBreakpoint: - resumeAction = DebuggerResumeAction.Continue; - bp = FindBreakpointHit(activityPosition); - doStop = (bp != null); - break; - - case WorkflowDebugMode.StepToNextActivity: - resumeAction = DebuggerResumeAction.StepOver; - doStop = (_nestedCallStack.Count <= _breakFrame); - break; - - case WorkflowDebugMode.StepIntoFunction: - resumeAction = DebuggerResumeAction.StepInto; - doStop = true; - break; - - case WorkflowDebugMode.StepOutOfFunction: - resumeAction = DebuggerResumeAction.StepOut; - doStop = (_nestedCallStack.Count < _breakFrame); - break; - } - - if (!doStop && - (mode == WorkflowDebugMode.StepOutOfFunction || - mode == WorkflowDebugMode.StepToNextActivity)) - { - // If we are in step over/out mode check break point, which - // overrides over/out action. - bp = FindBreakpointHit(activityPosition); - doStop = (bp != null); - } - - if (doStop) - { - // Update current break frame value. - _breakFrame = _nestedCallStack.Count; - Dbg.Assert(_breakFrame > 0, "Must always have a non zero call stack count at debugger stop."); - - return new DebuggerStopEventArgs( - CreateInvocationInfo(activityPosition), - (bp != null) ? new Collection() { bp } : new Collection(), - resumeAction); - } - - return null; - } - - private Breakpoint FindBreakpointHit( - ActivityPosition activityPosition) - { - // Line breakpoint. - foreach (var bp in _lineBreakPoints.Values) - { - LineBreakpoint lineBp = bp as LineBreakpoint; - if (CheckLineBreakpoint(lineBp, activityPosition.Line)) - { - return lineBp; - } - } - - // Command breakpoint. - // Variable breakpoint. - - return null; - } - - private bool CheckLineBreakpoint( - LineBreakpoint bp, - int activityLine) - { - if (bp == null || !bp.Enabled) { return false; } - - // Check for exact line match. - bool isGoodBreakpoint = (bp.Line == activityLine); - - if (!isGoodBreakpoint && (_validWFLineNumbers != null) && - ((_validWFLineNumbers.Count > 0) && (!_validWFLineNumbers.Contains(bp.Line)))) - { - // If requested breakpoint is not valid, check for a close line match. - // Currently this means a breakpoint within +/1 one script line. - isGoodBreakpoint = (Math.Abs(bp.Line - activityLine) == 1) ? true : false; - } - - if (!isGoodBreakpoint) - { - return isGoodBreakpoint; - } - - // Check to see if this is a valid line breakpoint with an Action - // script block and if so evaluate the script block. - if (bp.Action != null) - { - ErrorRecord errorRecord; - isGoodBreakpoint = CheckBreakpointAction(bp, out errorRecord); - - if (!isGoodBreakpoint && (errorRecord != null) && - (_host != null) && (_host.UI != null)) - { - _host.UI.WriteErrorLine(errorRecord.Exception.Message); - } - } - - if (!isGoodBreakpoint) - { - return isGoodBreakpoint; - } - - // Validate breakpoint with current source file. - if ((bp.Script != null) && - (_scriptName != null)) - { - isGoodBreakpoint = bp.Script.Equals(_scriptName, StringComparison.OrdinalIgnoreCase); - } - else - { - isGoodBreakpoint = false; - } - - return isGoodBreakpoint; - } - - private bool CheckBreakpointAction(LineBreakpoint bp, out ErrorRecord errorRecord) - { - errorRecord = null; - bool validBreakpoint = false; - - try - { - PSCommand cmd = new PSCommand(); - cmd.AddScript(bp.Action.ToString()); - if (CheckForWorkflowVariableChange(cmd, null, out errorRecord)) { return false; } - RunPowerShellCommand(cmd, null, null, false, true); - } - catch (BreakException) - { - validBreakpoint = true; - } - catch (Exception e) - { - // Don't throw exceptions on Participant tracker thread. - CheckForSevereException(e); - } - - return validBreakpoint; - } - - private void GetMetaDataAndCommonParameters( - Dictionary wfMetaData, - Dictionary commonParameters) - { - if (wfMetaData != null) - { - foreach (var item in wfMetaData) - { - _wfMetaVariables.Add(PSWorkflowApplicationInstance.TranslateMetaDataName(item.Key), item.Value); - } - } - - if (commonParameters != null) - { - foreach (var item in commonParameters) - { - if (!_wfMetaVariables.ContainsKey(item.Key)) - { - _wfMetaVariables.Add(item.Key, item.Value); - } - } - } - } - - private InvocationInfo CreateInvocationInfo( - ActivityPosition activityPosition) - { - string cmdName = activityPosition.Name; - int line = activityPosition.Line; - int col = activityPosition.Column; - - Dbg.Assert(!string.IsNullOrEmpty(_scriptName), "Script name should never be null during a debugger stop."); - - string script; - string scriptLine; - int endCol; - - if (string.IsNullOrEmpty(_script) || - (_scriptLines == null) || - (line < 1) || - (line > _scriptLines.Length)) - { - // Create a minimal InvocationInfo object without source information so that - // the user can get something (function name, line, column) on this workflow sequence point. - script = string.Empty; - scriptLine = string.Empty; - endCol = col; - } - else - { - // Otherwise provide full source information. - script = _script; - scriptLine = _scriptLines[line - 1]; - endCol = scriptLine.Length + 1; - } - - ScriptPosition scriptStartPosition = new ScriptPosition(_scriptName, line, col, scriptLine, script); - ScriptPosition scriptEndPosition = new ScriptPosition(_scriptName, line, endCol, scriptLine, script); - - InvocationInfo invocationInfo = InvocationInfo.Create( - new WorkflowInfo( - cmdName, - script, - ScriptBlock.Create(script), - (_xamlDefinition != null) ? _xamlDefinition : _outerFnXamlDefinition, - null), - new ScriptExtent( - scriptStartPosition, - scriptEndPosition) - ); - - // For remoting invocationInfo.DisplayScriptPosition does not serialize line string needed - // for display. Therefore set it to null so that ScriptPosition will be used which *does* - // pass the Line string. - invocationInfo.DisplayScriptPosition = null; - - return invocationInfo; - } - - private void AddBreakpoint(Breakpoint bp) - { - // Keep track of all script breakpoints from the parent so - // that *-PSBreakpoint commands can properly reflect this. - if (!_initalParentBreakpoints.ContainsKey(bp.Id)) - { - _initalParentBreakpoints.Add(bp.Id, bp); - } - - // Line breakpoints - LineBreakpoint lbp = bp as LineBreakpoint; - if (lbp != null) - { - if (!_lineBreakPoints.ContainsKey(lbp.Id)) { _lineBreakPoints.Add(lbp.Id, lbp); } - } - else - { - // Variable breakpoints - VariableBreakpoint vbp = bp as VariableBreakpoint; - if (vbp != null) - { - if (!_variableBreakPoints.ContainsKey(vbp.Id)) { _variableBreakPoints.Add(vbp.Id, vbp); } - } - else - { - // Command breakpoints - CommandBreakpoint cbp = bp as CommandBreakpoint; - if (cbp != null) - { - if (!_commandBreakpoints.ContainsKey(cbp.Id)) { _commandBreakpoints.Add(cbp.Id, cbp); } - } - } - } - } - - private void RemoveBreakpoint(int id) - { - _initalParentBreakpoints.Remove(id); - - if (!_lineBreakPoints.Remove(id)) - { - if (!_variableBreakPoints.Remove(id)) - { - _commandBreakpoints.Remove(id); - } - } - } - - /// - /// Lazily create Xaml related info. - /// - /// True if Xaml info is available. - private bool CheckXamlInfo() - { - if (!string.IsNullOrEmpty(_outerFnXamlDefinition)) - { - return true; - } - - if (_wfInstance.PSWorkflowDefinition != null && - _wfInstance.PSWorkflowDefinition.WorkflowXaml != null) - { - _outerFnXamlDefinition = _wfInstance.PSWorkflowDefinition.WorkflowXaml; - - // It is possible for the workflow definition to contain null/empty Xaml, - // such as when a workflow job is created from Xaml and not from PS script. - // In this case the debugger remains inactive. - return !string.IsNullOrEmpty(_outerFnXamlDefinition); - } - - return false; - } - - /// - /// Lazily create the job meta data variables. - /// - /// - private bool CheckWorkflowJob(string funcName) - { - if (_wfMetaVariables.Count > 0) - { - return true; - } - - if (_wfInstance.PSWorkflowJob != null && - _wfInstance.PSWorkflowJob.PSWorkflowJobDefinition != null) - { - // Look for script file and source info and if needed read script file. - // Update the function name-to-script map. - string scriptFile = _wfInstance.PSWorkflowJob.PSWorkflowJobDefinition.WorkflowScriptFile; - string script = _wfInstance.PSWorkflowJob.PSWorkflowJobDefinition.WorkflowFullScript; - if (string.IsNullOrEmpty(script) && !string.IsNullOrEmpty(scriptFile)) - { - script = ReadScriptFromFile(scriptFile); - } - - if (!string.IsNullOrEmpty(script)) - { - UpdateSourceMap( - _funcToSourceMap, - funcName, - script, - scriptFile, - _outerFnXamlDefinition); - } - - // Create job meta data. - GetMetaDataAndCommonParameters( - _wfInstance.PSWorkflowJob.JobMetadata, - _wfInstance.PSWorkflowJob.PSWorkflowCommonParameters); - - return true; - } - - return false; - } - - private void UpdateSourceMap( - Dictionary funcToSourceMap, - string funcName, - string script, - string scriptFile, - string xamlDefinition) - { - if (funcToSourceMap == null) { throw new PSArgumentNullException("funcToSourceMap"); } - if (funcName == null) { throw new PSArgumentNullException("funcName"); } - if (script == null) { throw new PSArgumentNullException("script"); } - - // Outer function name is always added. - DebugSource debugSource = new DebugSource(script, scriptFile, xamlDefinition); - funcToSourceMap.Add(funcName, debugSource); - - // Get list of workflow functions in the script AST and - // add reference to the debug source. - ParseScriptForMap(funcToSourceMap, script, debugSource); - } - - private void ParseScriptForMap( - Dictionary funcToSourceMap, - string script, - DebugSource debugSource) - { - ScriptBlock sb = ScriptBlock.Create(script); - ScriptBlockAst sbAst = sb.Ast as ScriptBlockAst; - if (sbAst != null) - { - if (sbAst.BeginBlock != null) - { - AddWorkflowFunctionToMap(sbAst.BeginBlock.Statements, funcToSourceMap, debugSource); - } - - if (sbAst.ProcessBlock != null) - { - AddWorkflowFunctionToMap(sbAst.ProcessBlock.Statements, funcToSourceMap, debugSource); - } - - if (sbAst.EndBlock != null) - { - AddWorkflowFunctionToMap(sbAst.EndBlock.Statements, funcToSourceMap, debugSource); - } - } - } - - private void AddWorkflowFunctionToMap( - ReadOnlyCollection statements, - Dictionary funcToSourceMap, - DebugSource debugSource) - { - if (statements == null) { return; } - - foreach (var statementAst in statements) - { - FunctionDefinitionAst fAst = statementAst as FunctionDefinitionAst; - if ((fAst != null) && (fAst.IsWorkflow)) - { - string fName = fAst.Name; - - if (funcToSourceMap.ContainsKey(fName)) - { - // Update existing source info. - var existingInfo = funcToSourceMap[fName]; - funcToSourceMap.Remove(fName); - - debugSource = new DebugSource( - debugSource.Script, // Update script - debugSource.ScriptFile, // Update script filename - existingInfo.XamlDefinition); // Keep XamlDefinition - } - - funcToSourceMap.Add(fName, debugSource); - - // Call this recursively for all statements in the workflow function to pick - // up any WF functions defined inside this WF function. - if (fAst.Body.BeginBlock != null) - { - AddWorkflowFunctionToMap(fAst.Body.BeginBlock.Statements, funcToSourceMap, debugSource); - } - - if (fAst.Body.ProcessBlock != null) - { - AddWorkflowFunctionToMap(fAst.Body.ProcessBlock.Statements, funcToSourceMap, debugSource); - } - - if (fAst.Body.EndBlock != null) - { - AddWorkflowFunctionToMap(fAst.Body.EndBlock.Statements, funcToSourceMap, debugSource); - } - } - } - } - - /// - /// UpdateScriptInfo - /// - private void UpdateScriptInfo(string funcName) - { - if (funcName.Equals(_scriptName, StringComparison.OrdinalIgnoreCase)) { return; } - - DebugSource debugSource; - if (!_funcToSourceMap.TryGetValue(funcName, out debugSource)) - { - // Minimal script info, just the Workflow function name. - _scriptName = funcName; - _script = null; - _scriptLines = null; - _xamlDefinition = null; - } - else - { - // Otherwise get full source script info. - _script = debugSource.Script; - _scriptName = !string.IsNullOrEmpty(debugSource.ScriptFile) ? debugSource.ScriptFile : funcName; - _scriptLines = _script.Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); - - _xamlDefinition = debugSource.XamlDefinition; - - if (!_funcToValidLineNumbersMap.TryGetValue(funcName, out _validWFLineNumbers)) - { - _validWFLineNumbers = CreateValidScriptLineSet(_xamlDefinition); - _funcToValidLineNumbersMap.Add(funcName, _validWFLineNumbers); - } - } - - // Reset parent debugger to show new source, if any. - Debugger parentDebugger = _parent; - if (parentDebugger != null) - { - parentDebugger.ResetCommandProcessorSource(); - } - } - - private SortedSet CreateValidScriptLineSet(string xamlDefinition) - { - if (xamlDefinition == null) { return null; } - - SortedSet validWFLineNumbers = new SortedSet(); - - try - { - XmlDocument doc = (XmlDocument)LanguagePrimitives.ConvertTo(xamlDefinition, typeof(XmlDocument), CultureInfo.InvariantCulture); - string nsName = "ns1"; - string ns = "clr-namespace:Microsoft.PowerShell.Activities;assembly=Microsoft.PowerShell.Activities"; - string query = string.Format(CultureInfo.InvariantCulture, @"//{0}:PowerShellValue/@Expression", nsName); - XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable); - nsMgr.AddNamespace(nsName, ns); - - // Search for all Xaml PowerShellValue/Expression node attributes. - XmlNodeList nodes = doc.SelectNodes(query, nsMgr); - foreach (XmlAttribute exprItem in nodes) - { - if (exprItem == null) { continue; } - string exprValue = exprItem.InnerText; - - // Look for line/col expression attribute symbol pattern and retrieve symbols. - if (System.Text.RegularExpressions.Regex.IsMatch(exprValue, @"^'\d+:\d+:\S+'$")) - { - string[] symbols = exprValue.Trim('\'').Split(':'); - - int nLine = -1; - try - { - nLine = Convert.ToInt32(symbols[0], CultureInfo.InvariantCulture); - } - catch (FormatException) - { } - catch (OverflowException) - { } - - // Add line number to list. - if (nLine > 0 && - !validWFLineNumbers.Contains(nLine)) - { - validWFLineNumbers.Add(nLine); - } - } - } - } - catch (ArgumentNullException) { } - catch (PSInvalidCastException) { } - catch (System.Xml.XPath.XPathException) { } - - return validWFLineNumbers; - } - - private static string ReadScriptFromFile(string scriptSource) - { - // If script content string is a file path then read the script. - try - { - return System.IO.File.ReadAllText(scriptSource); - } - catch (ArgumentException) { } - catch (System.IO.IOException) { } - catch (UnauthorizedAccessException) { } - catch (NotSupportedException) { } - catch (System.Security.SecurityException) { } - - return null; - } - - private DebuggerCommandResults InternalProcessCommand(PSCommand command, PSDataCollection output) - { - string cmd = command.Commands[0].CommandText.Trim(); - - if (cmd.Equals("prompt", StringComparison.OrdinalIgnoreCase)) - { - // WF prompt. - return HandlePromptCommand(command, output); - } - - if (cmd.Equals("exit", StringComparison.OrdinalIgnoreCase)) - { - // Ignore. - return new DebuggerCommandResults(DebuggerResumeAction.Continue, true); - } - - if (cmd.Equals("k", StringComparison.OrdinalIgnoreCase) || - cmd.Equals("Get-PSCallStack", StringComparison.OrdinalIgnoreCase)) - { - // WF call stack. - return HandleCallStack(output); - } - - return null; - } - - private DebuggerCommandResults HandlePromptCommand(PSCommand command, PSDataCollection output) - { - // Create WF Debug prompt. - string wfDebugPrompt; - string location = null; - object locationItem; - if (_wfMetaVariables.TryGetValue("PSComputerName", out locationItem)) - { - location = locationItem as string; - } - - wfDebugPrompt = string.Format(CultureInfo.InvariantCulture, - WFDebugPrompt, - !string.IsNullOrEmpty(location) ? location : string.Empty); - - // Run prompt command to get default prompt from runspace. - string defaultPrompt = null; - PSDataCollection promptOutput = new PSDataCollection(); - RunPowerShellCommand(command, null, promptOutput, false); - if (promptOutput.Count == 1) - { - defaultPrompt = promptOutput[0].BaseObject as string; - } - - if (defaultPrompt != null) - { - output.Add(wfDebugPrompt + defaultPrompt.TrimEnd() + "> "); - } - else - { - output.Add(wfDebugPrompt + DefaultWFPrompt); - } - - return new DebuggerCommandResults(null, true); - } - - private DebuggerCommandResults HandleCallStack(PSDataCollection output) - { - PSDataCollection callStack = GetCallStack().ToArray(); - if (callStack.Count > 0) - { - if ((_host != null) && (_host.UI != null) && - !(_host.GetType().FullName).Equals("System.Management.Automation.Remoting.ServerRemoteHost", - StringComparison.OrdinalIgnoreCase)) - { - // Use Out-Default to stream and format call stack data. - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Out-Default"); - - RunPowerShellCommand(cmd, callStack, output, false); - } - else - { - // Otherwise add directly to output. This is needed in the WF remote case because - // host output is blocked on the client during debugger stop. - // Use Out-String so that it gets formatted correctly. - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Out-String").AddParameter("Stream", true); - - RunPowerShellCommand(cmd, callStack, output, false); - } - } - - return new DebuggerCommandResults(null, true); - } - - private DebuggerCommandResults RunPowerShellCommand( - PSCommand command, - PSDataCollection input, - PSDataCollection output, - bool addToHistory, - bool allowBreakException = false) - { - // Wait for any current command to finish. - IAsyncResult async = _psAsyncResult; - if (async != null) - { - try - { - async.AsyncWaitHandle.WaitOne(); - } - catch (ObjectDisposedException) { } - } - - // Lazily create a runspace to run PS commands on. - if (_runspace == null || (_runspace.RunspaceStateInfo.State != RunspaceState.Opened)) - { - if (_runspace != null) { _runspace.Dispose(); } - - // Enforce the system lockdown policy if one is defined. - InitialSessionState iss = InitialSessionState.CreateDefault2(); - if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) - { - iss.LanguageMode = PSLanguageMode.ConstrainedLanguage; - } - - _runspace = (_host != null) ? - RunspaceFactory.CreateRunspace(_host, iss) : RunspaceFactory.CreateRunspace(iss); - - _runspace.Open(); - - SetPath(_runspace, _parentPath); - - CreateHelperFunctionsAndWorkflowMetaDataOnRunspace(_runspace); - - // Set breakpoints on runspace to support *-PSBreakpoint cmdlets. - _runspace.Debugger.SetBreakpoints(_initalParentBreakpoints.Values); - _runspace.Debugger.BreakpointUpdated += HandleRunspaceBreakpointUpdated; - } - - try - { - using (_psDebuggerCommand = System.Management.Automation.PowerShell.Create()) - { - // Runspace debugger must be disabled while running commands. - _runspace.Debugger.SetDebugMode(DebugModes.None); - _psDebuggerCommand.Runspace = _runspace; - _psDebuggerCommand.Commands = command; - foreach (var cmd in _psDebuggerCommand.Commands.Commands) - { - cmd.MergeMyResults(PipelineResultTypes.All, PipelineResultTypes.Output); - } - - // Default settings. - PSInvocationSettings settings = new PSInvocationSettings(); - settings.ExposeFlowControlExceptions = allowBreakException; - settings.AddToHistory = addToHistory; - - PSDataCollection collectOutput = (output != null) ? output : new PSDataCollection(); - - // Allow any exceptions to propagate. - _psAsyncResult = _psDebuggerCommand.BeginInvoke(input, collectOutput, settings, null, null); - _psDebuggerCommand.EndInvoke(_psAsyncResult); - } - } - finally - { - _psDebuggerCommand = null; - _psAsyncResult = null; - - // Restore workflow debugging. - _runspace.Debugger.SetDebugMode(DebugModes.LocalScript); - } - - return new DebuggerCommandResults(null, false); - } - - private void HandleRunspaceBreakpointUpdated(object sender, BreakpointUpdatedEventArgs e) - { - // Update local workflow breakpoints. - switch (e.UpdateType) - { - case BreakpointUpdateType.Set: - AddBreakpoint(e.Breakpoint); - break; - - case BreakpointUpdateType.Removed: - RemoveBreakpoint(e.Breakpoint.Id); - break; - } - - // Forward update to parent. - RaiseBreakpointUpdatedEvent(e); - } - - private void SetPath(Runspace runspace, PathInfo path) - { - if (path == null || - string.IsNullOrEmpty(path.Path)) - { - return; - } - - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - ps.AddCommand("Set-Location").AddParameter("Path", path.Path); - ps.Invoke(); - } - } - - private void CreateHelperFunctionsAndWorkflowMetaDataOnRunspace(Runspace runspace) - { - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = runspace; - - // Add workflow specific debugger functions. - foreach (var functionScript in System.Management.Automation.Internal.DebuggerUtils.GetWorkflowDebuggerFunctions()) - { - ps.Commands.Clear(); - ps.AddScript(functionScript); - ps.Invoke(); - } - - // Add meta workflow variables to runspace. - ps.Commands.Clear(); - ps.AddCommand("Set-DebuggerVariable").AddParameter("Variables", _wfMetaVariables); - ps.Invoke(); - - // Add private *this* debugger variable. - ps.Commands.Clear(); - ps.AddCommand("Set-Variable").AddParameter("Name", "PSWorkflowDebugger").AddParameter("Value", this).AddParameter("Visibility", "Private"); - ps.Invoke(); - } - } - - private void SetWorkflowVariablesToRunspace() - { - // Set workflow variables. - try - { - // Set workflow variables from activity context. - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Set-DebuggerVariable").AddParameter("Variables", _variables); - RunPowerShellCommand(cmd, null, null, false); - } - catch (Exception e) - { - // Don't throw exceptions on Participant tracker thread. - CheckForSevereException(e); - } - } - - private void SetWorkflowDebuggerContextVariable(DebuggerStopEventArgs args) - { - // Add context variable to variables collection. - PSDebugContext debugContext = new PSDebugContext(args.InvocationInfo, new List(args.Breakpoints)); - if (_variables.ContainsKey(DebugContextName)) - { - _variables[DebugContextName] = debugContext; - } - else - { - _variables.Add(DebugContextName, debugContext); - } - - // Set workflow debugger context variable to runspace. - try - { - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Set-Variable") - .AddParameter("Name", DebugContextName) - .AddParameter("Value", debugContext); - RunPowerShellCommand(cmd, null, null, false); - } - catch (Exception e) - { - // Don't throw exceptions on Participant tracker thread. - CheckForSevereException(e); - } - } - - private void RemoveWorkflowVariablesFromRunspace() - { - // Remove workflow variables. - try - { - PSCommand cmd = new PSCommand(); - cmd.AddCommand("Remove-DebuggerVariable").AddParameter("Name", _variables.Keys); - RunPowerShellCommand(cmd, null, null, false); - } - catch (Exception e) - { - // Don't throw exceptions on Participant tracker thread. - CheckForSevereException(e); - } - } - - /// - /// Helper method to check if user attempted to modify a workflow variable which - /// isn't supported. Write error message to host. - /// - private bool CheckForWorkflowVariableChange( - PSCommand command, - PSDataCollection output, - out ErrorRecord errorRecord) - { - errorRecord = null; - string variableName = string.Empty; - - // Searcher to detect a variable assignment. - // This is based on searcher from WorkflowJobConverter but is modified. - Func assignmentSearcher = (ast) => - { - AssignmentStatementAst assignment = ast as AssignmentStatementAst; - UnaryExpressionAst unaryExpression = ast as UnaryExpressionAst; - - if ((assignment == null) && (unaryExpression == null)) - { - return false; - } - - // Check if this is a unary assignment - if (unaryExpression != null) - { - VariableExpressionAst referenceVariable = unaryExpression.Child as VariableExpressionAst; - if (referenceVariable != null && String.Equals(referenceVariable.VariablePath.UserPath, variableName, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - // Check if this is a regular assignment - if (assignment == null) { return false; } - - // Capture $x = 10 - VariableExpressionAst variableExpression = assignment.Left as VariableExpressionAst; - string detectedVariableName = null; - - if (variableExpression == null) - { - // Capture [int] $x = 10 - ConvertExpressionAst convertExpression = assignment.Left as ConvertExpressionAst; - if (convertExpression != null) - { - variableExpression = convertExpression.Child as VariableExpressionAst; - } - } - - if (variableExpression != null) - { - detectedVariableName = variableExpression.VariablePath.UserPath; - } - else - { - return false; - } - - // See if it's the variable we're looking for - // Variable = - string workingVariableName = detectedVariableName; - - // Allow the "WORKFLOW:" scope qualifier in nested scopes - if (workingVariableName.StartsWith("WORKFLOW:", StringComparison.OrdinalIgnoreCase)) - { - workingVariableName = workingVariableName.Remove(0, "WORKFLOW:".Length); - } - - return String.Equals(workingVariableName, variableName, StringComparison.OrdinalIgnoreCase); - }; - - Dictionary allWFVariables = new Dictionary(_wfMetaVariables); - foreach (var wfVar in _variables) - { - if (!allWFVariables.ContainsKey(wfVar.Key)) - { - allWFVariables.Add(wfVar.Key, wfVar.Value); - } - } - - foreach (var cmd in command.Commands) - { - // Create scriptblock/AST for each command. - ScriptBlock sb; - try - { - sb = ScriptBlock.Create(cmd.CommandText); - } - catch (ParseException) - { - // Ignore parse exception here and let normal command - // processing handle potential incomplete parse condition. - return false; - } - - // Check assignments to known workflow variables. - foreach (var workflowVariable in allWFVariables) - { - variableName = workflowVariable.Key; - - Ast result = sb.Ast.Find(assignmentSearcher, searchNestedScriptBlocks: true); - if (result != null) - { - string msg = string.Format(System.Threading.Thread.CurrentThread.CurrentCulture, - Resources.DebuggerCannotModifyWFVars, variableName); - - errorRecord = new ErrorRecord( - new PSInvalidOperationException(msg), - "PSWorkflowDebuggerCannotModifyWorkflowVariables", - ErrorCategory.InvalidOperation, - this); - - return true; - } - } - } - - return false; - } - - private bool CheckAddToHistory(PSCommand command) - { - if (command.Commands.Count == 0) { return false; } - - return System.Management.Automation.Internal.DebuggerUtils.ShouldAddCommandToHistory( - command.Commands[0].CommandText); - } - - /// - /// Helper method to check for and rethrow severe exceptions. - /// - /// Exception to check - private static void CheckForSevereException(Exception e) - { - if (e is AccessViolationException || e is StackOverflowException) - { - throw e; - } - } - - #endregion - - #region Private Classes - - private class InvocationInfoComparer : IEqualityComparer - { - bool IEqualityComparer.Equals(InvocationInfo obj1, InvocationInfo obj2) - { - return obj1.MyCommand.Name.Equals(obj2.MyCommand.Name, StringComparison.OrdinalIgnoreCase); - } - - int IEqualityComparer.GetHashCode(InvocationInfo info) - { - return info.GetHashCode(); - } - } - - #endregion - - } // PSWorkflowDebugger -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDefinition.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDefinition.cs deleted file mode 100644 index 36e5ed1fb54..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowDefinition.cs +++ /dev/null @@ -1,91 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Management.Automation; - using System.Activities; - - /// - /// Define all the metadatas. - /// - public sealed class PSWorkflowDefinition - { - private Activity workflow; - private string workflowXaml; - private string runtimeAssemblyPath; - private Dictionary requiredAssemblies; - - /// - /// Workflow instance constructor. - /// - /// The workflow definition, represented in object-graph. - /// The workflow xaml definition. - /// The path to the runtime assembly. - /// Required assemblies paths - public PSWorkflowDefinition( - Activity workflow, - string workflowXaml, - string runtimeAssemblyPath, - Dictionary requiredAssemblies): this(workflow, workflowXaml, runtimeAssemblyPath) - { - this.requiredAssemblies = requiredAssemblies; - } - - /// - /// Workflow instance constructor. - /// - /// The workflow definition, represented in object-graph. - /// The workflow xaml definition. - /// The path to the runtime assembly. - public PSWorkflowDefinition( - Activity workflow, - string workflowXaml, - string runtimeAssemblyPath) - { - this.workflow = workflow; - this.workflowXaml = workflowXaml; - this.runtimeAssemblyPath = runtimeAssemblyPath; - } - - /// - /// Gets sets the workflow. - /// - public Activity Workflow - { - get { return this.workflow; } - set { this.workflow = value; } - } - - /// - /// Gets sets the workflow xaml. - /// - public string WorkflowXaml - { - get { return this.workflowXaml; } - set { this.workflowXaml = value; } - } - - /// - /// Gets sets the runtime assembly path. - /// - public string RuntimeAssemblyPath - { - get { return this.runtimeAssemblyPath; } - set { this.runtimeAssemblyPath = value; } - } - - /// - /// Gets sets the required assemblies. - /// - public Dictionary RequiredAssemblies - { - get { return this.requiredAssemblies; } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowInstance.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowInstance.cs deleted file mode 100644 index 4198665363c..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowInstance.cs +++ /dev/null @@ -1,2613 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Diagnostics; -using System.Management.Automation.Runspaces; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Collections.Generic; - using System.Activities; - using System.Management.Automation; - using System.Management.Automation.Tracing; - using System.Globalization; - using System.Diagnostics.CodeAnalysis; - using System.Activities.Tracking; - using System.IO; - using System.Reflection; - using Microsoft.PowerShell.Activities; - using Microsoft.PowerShell.Commands; - using System.Timers; - using System.Collections.ObjectModel; - using System.Activities.Hosting; - using Dbg = System.Diagnostics.Debug; - using System.Security.Principal; - using System.Management.Automation.Language; - using System.Runtime.Serialization; - using System.Linq; - - /// - /// Specifies the action that occurs when a workflow becomes idle when persistence is allowed. - /// - public enum PSPersistableIdleAction - { - /// - /// No or null action is defined so fall back to default. - /// - NotDefined = 0, - - /// - /// Specifies that no action is taken. - /// - None = 1, - - /// - /// Specifies that the System.Activities.WorkflowApplication should persist the workflow. - /// - Persist = 2, - - /// - /// Specifies that the System.Activities.WorkflowApplication should persist and unload the workflow. - /// The job will remain in running state because async operation (out of proc or remote operation) is in progress. - /// The System.Activities.WorkflowApplication will be loaded when async operation gets completed. - /// - Unload = 3, - - /// - /// Specifies that the System.Activities.WorkflowApplication should persist and unload the workflow and Job is marked as suspended. - /// - Suspend = 4, - - } - - /// - /// This class encapsulate the guid to avoid the confusion be job instance id and workflow instance id. - /// - public sealed class PSWorkflowId - { - private Guid _guid; - - /// - /// Default constructor - /// - public PSWorkflowId() - { - _guid = new Guid(); - } - - /// - /// Constructor which takes guid. - /// - /// - public PSWorkflowId(Guid value) - { - _guid = value; - } - - /// - /// NewWorkflowGuid - /// - /// - public static PSWorkflowId NewWorkflowGuid() - { - return new PSWorkflowId(Guid.NewGuid()); - } - - /// - /// Gets Guid - /// - public Guid Guid - { - get - { - return _guid; - } - } - } - - /// - /// WorkflowOnHandledErrorAction - /// - public enum WorkflowUnhandledErrorAction - { - /// - /// Suspend - /// - Suspend = 0, - - /// - /// Stop - /// - Stop = 1, - - /// - /// Terminate - /// - Terminate = 2, - }; - - /// - /// Possible workflow instance creation modes. - /// - internal enum WorkflowInstanceCreationMode - { - /// - /// Workflow instance created normally. - /// - Normal = 0, - - /// - /// Workflow instance created normally. - /// - AfterCrashOrShutdown = 1, - }; - - /// - /// PSWorkflowRemoteActivityState - /// - public sealed class PSWorkflowRemoteActivityState - { - // the remote runspace instance ids collection - private Dictionary>> _remoteRunspaceIdCollection; - private readonly PSWorkflowInstanceStore _store; - private bool _internalUnloaded = false; - // sync object is required to synchronize the access to _remoteRunspaceIdCollection from multiple parallel tasks execution - private object _syncObject = new object(); - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// PSWorkflowRemoteActivityState constructor - /// - /// - internal PSWorkflowRemoteActivityState(PSWorkflowInstanceStore store) - { - _store = store; - _remoteRunspaceIdCollection = new Dictionary>>(); - } - - /// - /// Creates a PSWorkflowRemoteActivityState for a workflow instance based on deserialized object - /// - /// - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the PowerShell Workflow extensibility.")] - public PSWorkflowRemoteActivityState(PSWorkflowInstanceStore store, Dictionary>> deserializedRemoteActivityState) - { - _store = store; - - if (deserializedRemoteActivityState == null) - throw new ArgumentNullException("deserializedRemoteActivityState"); - - _remoteRunspaceIdCollection = deserializedRemoteActivityState; - } - - /// - /// Retrieves RemoteActivityState object to be serialized - /// - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is forced by the PowerShell Workflow extensibility.")] - public Dictionary>> GetSerializedData() - { - lock (_syncObject) - { - // Get the copy of current _remoteRunspaceIdCollection for serialization - return new Dictionary>>(_remoteRunspaceIdCollection); - } - } - - /// - /// InternalUnloaded property - /// - internal bool InternalUnloaded - { - get - { - lock (_syncObject) - { - return _internalUnloaded; - } - } - set - { - lock (_syncObject) - { - _internalUnloaded = value; - } - } - } - - /// - /// GetRemoteActivityRunspaceEntry - /// - /// - /// - /// task entry object - internal object GetRemoteActivityRunspaceEntry(string activityId, int taskId) - { - object taskRunspaceId = null; - - lock (_syncObject) - { - Dictionary> runspaceIdCollection; - if (_remoteRunspaceIdCollection.TryGetValue(activityId, out runspaceIdCollection)) - { - if (runspaceIdCollection != null) - { - Tuple taskTuple; - runspaceIdCollection.TryGetValue(taskId, out taskTuple); - - if (taskTuple != null) - taskRunspaceId = taskTuple.Item1; - } - } - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "RemoteActivityState: runspace entry for taskid: {0}, taskEntry: {1}", taskId, taskRunspaceId)); - return taskRunspaceId; - } - - /// - /// SetRemoteActivityRunspaceEntry - /// - /// - /// - /// task state can be "NotStarted", Guid, or "Completed" - /// computer name of the task - internal void SetRemoteActivityRunspaceEntry(string activityId, int taskId, object taskState, string computerName) - { - lock (_syncObject) - { - Dictionary> activityTasksRunspaceIdCollection; - _remoteRunspaceIdCollection.TryGetValue(activityId, out activityTasksRunspaceIdCollection); - - if (activityTasksRunspaceIdCollection == null) - { - activityTasksRunspaceIdCollection = new Dictionary>(); - _remoteRunspaceIdCollection.Add(activityId, activityTasksRunspaceIdCollection); - } - else - { - // computerName will be passed as null during runspace instance id assignment and task completion - // So retrieving it from task entry. - if (computerName == null) - { - Tuple taskTuple; - activityTasksRunspaceIdCollection.TryGetValue(taskId, out taskTuple); - if (taskTuple != null) - { - computerName = taskTuple.Item2; - } - } - - // Remove the taskid entry if exists - activityTasksRunspaceIdCollection.Remove(taskId); - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "RemoteActivityState.SetRemoteActivityRunspaceEntry: runspace entry for taskid: {0}, taskState: {1}, computerName: {2}", taskId, taskState, computerName)); - - activityTasksRunspaceIdCollection.Add(taskId, new Tuple(taskState, computerName)); - } - - if (InternalUnloaded) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "RemoteActivityState.SetRemoteActivityRunspaceEntry: persisting the Streams and RemoteActivityState")); - - // Streams and ActivityState needs to be persisted when workflow application is already unloaded internally - // Streams will have the results from task completion - _store.Save(WorkflowStoreComponents.ActivityState | WorkflowStoreComponents.Streams); - } - } - - /// - /// RemoteActivityResumeRequired - /// - /// - /// - /// - internal bool RemoteActivityResumeRequired(PSActivity activity, bool duringResumeBookmark) - { - bool activityResumeRequired = false; - if (activity is PSRemotingActivity) - { - lock (_syncObject) - { - if (_remoteRunspaceIdCollection.ContainsKey(activity.Id)) - { - // if all commands are completed remove the activity instance id's entry from remoteRunspaceCollection - // otherwise restart the activity execution to reconnect to managed nodes. - Dictionary> commandRunspaceIdCollection; - _remoteRunspaceIdCollection.TryGetValue(activity.Id, out commandRunspaceIdCollection); - if (commandRunspaceIdCollection != null) - { - if (commandRunspaceIdCollection.Any(o => o.Value.Item1 is Guid)) - { - activityResumeRequired = true; - } - else if (commandRunspaceIdCollection.Any(o => (o.Value.Item1 is string && o.Value.Item1.ToString().Equals("notstarted")))) - { - activityResumeRequired = true; - } - } - - // This method is first time called from OnResumeBookmark, if no resume required it's entry - // needs to be removed fromt the collection as activity execution has finished. - // When an activity entry is removed, remoteActivityState will be persisted - // as part of whole workflow application persistence at the end of activity completion - if (activityResumeRequired == false) - _remoteRunspaceIdCollection.Remove(activity.Id); - } - else if (duringResumeBookmark) - { - // Activity needs to be restarted/resumed if the process got crashed/terminated just after activity is bookmarked and - // in this case there will be no entry for this activity id in _remoteRunspaceIdCollection - activityResumeRequired = true; - } - } - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "RemoteActivityState.RemoteActivityResumeRequired returning activityResumeRequired: {0}", activityResumeRequired)); - return activityResumeRequired; - } - } - - /// - /// Collects all the information related to a workflow instance. - /// - internal class PSWorkflowApplicationInstance : PSWorkflowInstance - { - #region Private Members - - /// - /// Tracer initialization. - /// - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// Workflow Application executes the workflow and it is part of CLR 4.0. - /// - private WorkflowApplication workflowApplication; - - /// - /// The Guid, which represents the unique instance of a workflow. - /// - private Guid id; - - /// - /// Workflow definition. - /// - private PSWorkflowDefinition _definition; - - /// - /// Workflow output, which represent all streams from the workflow. - /// - private PowerShellStreams _streams; - - /// - /// Workflow error exception, which represent the terminating error workflow. - /// - private Exception _errorException; - - /// - /// the remote runspace instance ids collection - /// - private PSWorkflowRemoteActivityState _remoteActivityState; - - /// - /// Workflow metadatas. - /// - private PSWorkflowContext _metadatas; - - /// - /// Workflow debugger. - /// - private PSWorkflowDebugger _debugger; - - /// - /// Workflow timers. - /// - private PSWorkflowTimer _timers; - - /// - /// The workflow creation mode. - /// - private WorkflowInstanceCreationMode creationMode; - - private Dictionary asyncExecutionCollection; - - private bool PersistAfterNextPSActivity; - private bool suspendAtNextCheckpoint = false; - - - private readonly PSWorkflowInstanceStore _stores; - private static readonly Tracer _structuredTracer = new Tracer(); - private PSWorkflowJob _job; - - private bool errorExceptionLoadCalled = false; - - private bool _suppressTerminateError; - - private void HandleWorkflowApplicationCompleted(WorkflowApplicationCompletedEventArgs e) - { - if (Disposed) - return; - - _structuredTracer.Correlate(); - - if (e.CompletionState == ActivityInstanceState.Closed) - { - if (System.Threading.Interlocked.CompareExchange(ref terminalStateHandled, Handled, NotHandled) == Handled) - return; - - try - { - Tracer.WriteMessage("Workflow Application is completed and is in closed state."); - - Tracer.WriteMessage("Flatting out the PSDataCollection returned outputs."); - - foreach (KeyValuePair outvariable in e.Outputs) - { - if (outvariable.Value != null) - { - if (outvariable.Value is PSDataCollection) - { - PSDataCollection outCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(outCollection != null, "outCollection should not be NULL"); - - foreach (PSObject obj in outCollection) - { - Streams.OutputStream.Add(obj); - } - outCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection errorCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(errorCollection != null, "errorCollection should not be NULL"); - - foreach (ErrorRecord obj in errorCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - errorCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection warningCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(warningCollection != null, "warningCollection should not be NULL"); - - foreach (WarningRecord obj in warningCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - warningCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection progressCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(progressCollection != null, "progressCollection should not be NULL"); - - foreach (ProgressRecord obj in progressCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - progressCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection verboseCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(verboseCollection != null, "verboseCollection should not be NULL"); - - foreach (VerboseRecord obj in verboseCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - verboseCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection debugCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(debugCollection != null, "debugCollection should not be NULL"); - - foreach (DebugRecord obj in debugCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - debugCollection.Clear(); - } - else if (outvariable.Value is PSDataCollection) - { - PSDataCollection informationCollection = outvariable.Value as PSDataCollection; - - Dbg.Assert(informationCollection != null, "informationCollection should not be NULL"); - - foreach (InformationRecord obj in informationCollection) - { - Streams.OutputStream.Add(PSObject.AsPSObject(obj)); - } - informationCollection.Clear(); - } - else - { - // pass the object as is to the output stream - this.Streams.OutputStream.Add(PSObject.AsPSObject(outvariable.Value)); - } - } - } - } - catch (Exception exception) - { - Dbg.Assert(false, "An exception is raised after a workflow completed successfully. This is most likely a case of exception eating, check the exception details"); - Tracer.TraceException(exception); - } - - State = JobState.Completed; - this.PerformTaskAtTerminalState(); - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnCompleted != null) - this.OnCompleted(this); - } - - if (e.CompletionState == ActivityInstanceState.Faulted) - { - HandleWorkflowApplicationFaultedState(e.TerminationException); - } - - if (e.CompletionState == ActivityInstanceState.Canceled) - { - this.HandleWorkflowApplicationCanceled(); - } - } - - private void HandleWorkflowApplicationCanceled() - { - if (System.Threading.Interlocked.CompareExchange(ref terminalStateHandled, Handled, NotHandled) == Handled) - return; - - Tracer.WriteMessage("Workflow Application is completed in Canceled state."); - - State = JobState.Stopped; - this.PerformTaskAtTerminalState(); - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnStopped != null) - this.OnStopped(this); - } - - private ErrorRecord GetInnerErrorRecord(Exception exception) - { - IContainsErrorRecord nestedException = exception as IContainsErrorRecord; - if (nestedException == null) return null; - return nestedException.ErrorRecord; - } - - private void HandleWorkflowApplicationAborted(WorkflowApplicationAbortedEventArgs e) - { - if (Disposed) - return; - - _structuredTracer.Correlate(); - Tracer.WriteMessage("Workflow Application is completed in Aborted state."); - - // if the suspend in progress and there is some error it result in the aborted event - // explicit faulting the workflow. - if (this.callSuspendDelegate) - { - this.callSuspendDelegate = false; - HandleWorkflowApplicationFaultedState(e.Reason); - return; - } - - - // if there is an exception in the canceling the workflow the WF application throws abort event instead on canceled event. - if (_job.JobStateInfo.State == JobState.Stopping) - { - HandleWorkflowApplicationCanceled(); - return; - } - - this.Error = e.Reason; - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnAborted != null) - this.OnAborted(e.Reason, this); - } - - private UnhandledExceptionAction HandleWorkflowApplicationUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e) - { - if (Disposed) - return UnhandledExceptionAction.Terminate; - - _structuredTracer.Correlate(); - Tracer.WriteMessage("Workflow Application is completed in Unhandled exception state."); - - if (PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.PSWorkflowErrorAction) && PSWorkflowContext.PSWorkflowCommonParameters[Constants.PSWorkflowErrorAction] != null) - { - WorkflowUnhandledErrorAction action = (WorkflowUnhandledErrorAction)PSWorkflowContext.PSWorkflowCommonParameters[Constants.PSWorkflowErrorAction]; - - switch (action) - { - case WorkflowUnhandledErrorAction.Stop: - return UnhandledExceptionAction.Cancel; - - case WorkflowUnhandledErrorAction.Suspend: - return UnhandledExceptionAction.Abort; - - case WorkflowUnhandledErrorAction.Terminate: - return UnhandledExceptionAction.Terminate; - } - } - - return UnhandledExceptionAction.Terminate; - } - - private void HandlePersistence(object state) - { - if (Disposed) - return; - - // this lock will ensure the synchronization between the persistence and the force suspend (abort) method. - // if persistence is going on then it will hold the execution of force suspend - lock (ReactivateSync) - { - if (Disposed) - return; - - // if force suspend or abort has already been called IsTerminalStateAction will be true and then - // there is no need to do the persistence. - if (IsTerminalStateAction) - return; - - - if (_job.JobStateInfo.State == JobState.Running && this.workflowApplication != null) - { - try - { - Tracer.WriteMessage("PSWorkflowApplicationInstance", "HandlePersistence", id, "Persisting the workflow."); - this.workflowApplication.Persist(); - - } - catch (Exception e) - { - Tracer.TraceException(e); - SafelyHandleFaultedState(e); - return; - } - - try - { - foreach (BookmarkInfo b in this.workflowApplication.GetBookmarks()) - { - if (b.BookmarkName.Contains(PSActivity.PSPersistBookmarkPrefix)) - this.workflowApplication.ResumeBookmark(b.BookmarkName, null); - } - } - catch (Exception e) - { - // this may occur in the race condition if there are many persistable workflow and then you tried to shutdown them. - Tracer.WriteMessage("PSWorkflowApplicationInstance", "HandlePersistence", id, "There has been exception while persisting the workflow in the background thread."); - Tracer.TraceException(e); - } - } - } - } - - private void SafelyHandleFaultedState(Exception exception) - { - try - { - HandleWorkflowApplicationFaultedState(exception); - } - catch (Exception e) - { - // this may occur in the race condition if there is a persistable workflow which is getting stopped and at the same persist is called. - Tracer.WriteMessage("PSWorkflowApplicationInstance", "SafelyHandleFaultedState", id, "There has been exception while marking the workflow in faulted state in the background thread."); - Tracer.TraceException(e); - } - } - - private bool callSuspendDelegate = false; - - private PersistableIdleAction HandleWorkflowApplicationPersistableIdle(WorkflowApplicationIdleEventArgs e) - { - if (Disposed) - return PersistableIdleAction.None; - - _structuredTracer.Correlate(); - - PSPersistableIdleAction action = PSPersistableIdleAction.NotDefined; - - if (this.OnPersistableIdleAction != null) - action = this.OnPersistableIdleAction(e.Bookmarks, this.suspendAtNextCheckpoint, this); - - if(action == PSPersistableIdleAction.NotDefined) // fall back default handler - action = _job.GetPersistableIdleAction(e.Bookmarks, this.suspendAtNextCheckpoint); - - switch (action) - { - case PSPersistableIdleAction.None: - return PersistableIdleAction.None; - - case PSPersistableIdleAction.Persist: - System.Threading.ThreadPool.QueueUserWorkItem(this.HandlePersistence); - return PersistableIdleAction.None; - - case PSPersistableIdleAction.Unload: - if (Runtime.Configuration.PSWorkflowApplicationPersistUnloadTimeoutSec <= 0) - { - this.StartPersistUnloadWithZeroSeconds(); - return PersistableIdleAction.Unload; - } - else - { - this.StartPersistUnloadTimer(Runtime.Configuration.PSWorkflowApplicationPersistUnloadTimeoutSec); - return PersistableIdleAction.None; - } - - case PSPersistableIdleAction.Suspend: - this.callSuspendDelegate = true; - return PersistableIdleAction.Unload; - } - - return PersistableIdleAction.None; - } - - private void HandleWorkflowApplicationIdle(WorkflowApplicationIdleEventArgs e) - { - if (Disposed) - return; - - _structuredTracer.Correlate(); - Tracer.WriteMessage("Workflow Application is idle."); - - // there might be a possibility that stop job is being called by wfApp is Idle handling it properly so all Async execution . - if (_job.JobStateInfo.State == JobState.Stopping) - { - this.StopBookMarkedWorkflow(); - return; - } - - if (this.OnIdle != null) - this.OnIdle(e.Bookmarks, this); - } - - private void HandleWorkflowApplicationUnloaded(WorkflowApplicationEventArgs e) - { - if (Disposed) - return; - - _structuredTracer.Correlate(); - Tracer.WriteMessage("Workflow Application is unloaded."); - - if (this.callSuspendDelegate) - { - this.callSuspendDelegate = false; - // suspend logic - if (this.OnSuspended != null) - { - this.OnSuspended(this); - } - } - - if (this.OnUnloaded != null) - this.OnUnloaded(this); - } - - private void SubscribeWorkflowApplicationEvents() - { - this.workflowApplication.Completed += HandleWorkflowApplicationCompleted; - this.workflowApplication.Aborted += HandleWorkflowApplicationAborted; - this.workflowApplication.OnUnhandledException += HandleWorkflowApplicationUnhandledException; - this.workflowApplication.PersistableIdle += HandleWorkflowApplicationPersistableIdle; - this.workflowApplication.Idle += HandleWorkflowApplicationIdle; - this.workflowApplication.Unloaded += HandleWorkflowApplicationUnloaded; - } - - private void DisposeWorkflowApplication() - { - if (this.workflowApplication != null) - { - this.workflowApplication.Completed -= HandleWorkflowApplicationCompleted; - this.workflowApplication.Aborted -= HandleWorkflowApplicationAborted; - this.workflowApplication.OnUnhandledException -= HandleWorkflowApplicationUnhandledException; - this.workflowApplication.PersistableIdle -= HandleWorkflowApplicationPersistableIdle; - this.workflowApplication.Idle -= HandleWorkflowApplicationIdle; - this.workflowApplication.Unloaded -= HandleWorkflowApplicationUnloaded; - this.workflowApplication = null; - } - } - - private void OnSuspendUnloadComplete(IAsyncResult asyncResult) - { - if (Disposed) return; - - try - { - WorkflowApplication wfa = this.workflowApplication; - if (wfa != null) - { - wfa.EndUnload(asyncResult); - } - } - catch (Exception e) - { - Tracer.WriteMessage("PSWorkflowInstance", "DoSuspendInstance", id, "Not able to unload workflow application in a given timeout."); - Tracer.TraceException(e); - - HandleWorkflowApplicationFaultedState(e); - - return; - } - - this.ConfigureTimerOnUnload(); - this.DisposeWorkflowApplication(); - - if (this.OnSuspended != null) - this.OnSuspended(this); - } - - private int terminalStateHandled = NotHandled; - private const int NotHandled = 0; - private const int Handled = 1; - private void HandleWorkflowApplicationFaultedState(Exception e) - { - if (System.Threading.Interlocked.CompareExchange(ref terminalStateHandled, Handled, NotHandled) == Handled) - return; - - Tracer.WriteMessage("Workflow Application is completed in Faulted state."); - - // there might be possible of race condition here in case of Winrm shutdown. if the activity is - // executing on loop back winrm process so there might be possibility that the winrm shuts down the - // activity process fist and causing the workflow to fail in the M3P process. - // in order to avoid those situations we will ignore the remote exception if the shutdown is in progress - if (WorkflowJobSourceAdapter.GetInstance().IsShutdownInProgress) - { - if (e.GetType() == typeof(RemoteException)) - { - Tracer.WriteMessage("PSWorkflowApplicationInstance", "HandleWorkflowApplicationFaultedState", id, "Since we are in shuting down mode so ignoring the remote exception"); - Tracer.TraceException(e); - - // no need to proceed any further - return; - } - } - - // before we start handling the faulted state we need to cancel - // all running activities. Activities may be running if there is a - // parallel statement involved - StopAllAsyncExecutions(); - - State = JobState.Failed; - Exception reason = null; - - if (!_suppressTerminateError) - { - reason = e; - - ErrorRecord failureErrorRecord = GetInnerErrorRecord(reason); - if (failureErrorRecord != null) - { - reason = failureErrorRecord.Exception; - - if (PSWorkflowJob.SynchronousExecution && Streams.ErrorStream.IsOpen) - { - // If we're running synchronously from the commandline, we want to see the error - // record from the workflow without the Receive-Job decoration. Receive-Job will - // decorate the reason, so do not send it as the reason. - // Note that if the error collection is closed, we do not want to lose this data, so - // pass the exception despite the bad decoration. - Streams.ErrorStream.Add(failureErrorRecord); - reason = null; - } - } - else - { - // No error record, a raw exception was thrown. See if we have location - // information that we can wrap it in. - HostSettingCommandMetadata commandMetadata = null; - - if (_paramDefaults != null) - commandMetadata = _paramDefaults.HostCommandMetadata; - - if (commandMetadata != null) - { - ScriptPosition scriptStart = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.StartLineNumber, - commandMetadata.StartColumnNumber, - null); - ScriptPosition scriptEnd = new ScriptPosition( - commandMetadata.CommandName, - commandMetadata.EndLineNumber, - commandMetadata.EndColumnNumber, - null); - ScriptExtent extent = new ScriptExtent(scriptStart, scriptEnd); - - reason = new JobFailedException(reason, extent); - } - } - } - - this.Error = reason; - - this.PerformTaskAtTerminalState(); - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnFaulted != null) - this.OnFaulted(reason, this); - } - - private void CheckDisposed() - { - if (Disposed) - { - Debug.Assert(Disposed, "PSWorkflowInstance has been disposed " + Id); - } - } - - private void PerformCleanupAtTerminalState() - { - DisposeWorkflowApplication(); - ConfigureTimerOnUnload(); - if (_streams != null) - UnregisterHandlersForDataAdding(_streams); - } - - #endregion Private Members - - #region Internal Members - - /// - /// Workflow instance constructor. - /// - /// - /// The workflow definition. - /// The metadata which includes parameters etc. - /// This is input coming from pipeline, which is added to the input stream. - /// - internal PSWorkflowApplicationInstance( - PSWorkflowRuntime runtime, - PSWorkflowDefinition definition, - PSWorkflowContext metadata, - PSDataCollection pipelineInput, - PSWorkflowJob job) - { - Tracer.WriteMessage("Creating Workflow instance."); - InitializePSWorkflowApplicationInstance(runtime); - this._definition = definition; - this._metadatas = metadata; - this._streams = new PowerShellStreams(pipelineInput); - RegisterHandlersForDataAdding(_streams); - this._timers = new PSWorkflowTimer(this); - this.creationMode = WorkflowInstanceCreationMode.Normal; - - _job = job; - _stores = Runtime.Configuration.CreatePSWorkflowInstanceStore(this); - - this._remoteActivityState = new PSWorkflowRemoteActivityState(_stores); - } - - private void InitializePSWorkflowApplicationInstance(PSWorkflowRuntime runtime) - { - if (runtime == null) - throw new ArgumentNullException("runtime"); - - this.PersistAfterNextPSActivity = false; - this.suspendAtNextCheckpoint = false; - Runtime = runtime; - - this.asyncExecutionCollection = new Dictionary(); - this.ForceDisableStartOrEndPersistence = false; - - if (Runtime.Configuration.PSWorkflowApplicationPersistUnloadTimeoutSec > 0) - { - PersistUnloadTimer = new Timer(Convert.ToDouble(Runtime.Configuration.PSWorkflowApplicationPersistUnloadTimeoutSec * 1000)); - PersistUnloadTimer.Elapsed += new ElapsedEventHandler(PersistUnloadTimer_Elapsed); - PersistUnloadTimer.AutoReset = false; - } - - this._debugger = new PSWorkflowDebugger(this); - } - - private void RegisterHandlersForDataAdding(PowerShellStreams streams) - { - streams.OutputStream.DataAdding += HandleOutputDataAdding; - streams.ErrorStream.DataAdding += HandleErrorDataAdding; - streams.DebugStream.DataAdding += HandleInformationalDataAdding; - streams.VerboseStream.DataAdding += HandleInformationalDataAdding; - streams.WarningStream.DataAdding += HandleInformationalDataAdding; - streams.ProgressStream.DataAdding += HandleProgressDataAdding; - streams.InformationStream.DataAdding += HandleInformationDataAdding; - } - - private void UnregisterHandlersForDataAdding(PowerShellStreams streams) - { - if (streams.OutputStream != null) - streams.OutputStream.DataAdding -= HandleOutputDataAdding; - if (streams.ErrorStream != null) - streams.ErrorStream.DataAdding -= HandleErrorDataAdding; - if (streams.DebugStream != null) - streams.DebugStream.DataAdding -= HandleInformationalDataAdding; - if (streams.VerboseStream != null) - streams.VerboseStream.DataAdding -= HandleInformationalDataAdding; - if (streams.WarningStream != null) - streams.WarningStream.DataAdding -= HandleInformationalDataAdding; - if (streams.ProgressStream != null) - streams.ProgressStream.DataAdding -= HandleProgressDataAdding; - if (streams.InformationStream != null) - streams.InformationStream.DataAdding -= HandleInformationDataAdding; - } - - private const string LocalHost = "localhost"; - private void HandleOutputDataAdding(object sender, DataAddingEventArgs e) - { - PSObject psObject = (PSObject) e.ItemAdded; - - if (psObject == null) return; - - PSActivity.AddIdentifierInfoToOutput(psObject, JobInstanceId, LocalHost); - } - - private void HandleErrorDataAdding(object sender, DataAddingEventArgs e) - { - ErrorRecord errorRecord = (ErrorRecord)e.ItemAdded; - if (errorRecord == null) return; - - PSActivity.AddIdentifierInfoToErrorRecord(errorRecord, LocalHost, JobInstanceId); - } - - private void HandleProgressDataAdding(object sender, DataAddingEventArgs e) - { - ProgressRecord progressRecord = (ProgressRecord)e.ItemAdded; - if (progressRecord == null) return; - - progressRecord.CurrentOperation = PSActivity.AddIdentifierInfoToString(JobInstanceId, LocalHost, - progressRecord.CurrentOperation); - } - - private void HandleInformationDataAdding(object sender, DataAddingEventArgs e) - { - InformationRecord informationRecord = (InformationRecord)e.ItemAdded; - if (informationRecord == null) return; - - informationRecord.Source = PSActivity.AddIdentifierInfoToString(JobInstanceId, LocalHost, - informationRecord.Source); - } - - private void HandleInformationalDataAdding(object sender, DataAddingEventArgs e) - { - InformationalRecord informationalRecord = (InformationalRecord)e.ItemAdded; - if (informationalRecord == null) return; - - informationalRecord.Message = PSActivity.AddIdentifierInfoToString(JobInstanceId, LocalHost, - informationalRecord.Message); - } - - private Guid JobInstanceId - { - get - { - return _job != null ? _job.InstanceId : Guid.Empty; - } - } - - /// - /// Workflow instance constructor for shutdown or crashed workflows. - /// - /// - /// - internal PSWorkflowApplicationInstance(PSWorkflowRuntime runtime, PSWorkflowId instanceId) - { - Tracer.WriteMessage("Creating Workflow instance after crash and shutdown workflow."); - InitializePSWorkflowApplicationInstance(runtime); - this._definition = null; - this._metadatas = null; - this._streams = null; - this._timers = null; - this.id = instanceId.Guid; - this.creationMode = WorkflowInstanceCreationMode.AfterCrashOrShutdown; - - _stores = Runtime.Configuration.CreatePSWorkflowInstanceStore(this); - this._remoteActivityState = null; - } - - /// - /// Gets the Guid of workflow instance. - /// - internal override Guid Id - { - get { return this.id; } - } - - - /// - /// Load instance for resuming the workflow - /// - internal override void DoLoadInstanceForReactivation() - { - CheckDisposed(); - - try - { - lock (SyncLock) - { - Tracer.WriteMessage("Loading for Workflow resumption."); - - if (this.PSWorkflowDefinition.Workflow == null) - { - ArgumentException exception = new ArgumentException(Resources.NoWorkflowProvided); - Tracer.TraceException(exception); - throw exception; - } - - this.workflowApplication = new WorkflowApplication(this.PSWorkflowDefinition.Workflow); - this.wfAppNeverLoaded = false; - SubscribeWorkflowApplicationEvents(); - this.ConfigureAllExtensions(); - - // loading the workflow context from the store - this.workflowApplication.Load(this.id); - - SetInternalUnloaded(false); - - Tracer.WriteMessage("Workflow is loaded for reactivation, Guid = " + this.id.ToString("D", CultureInfo.CurrentCulture)); - } - } - catch(Exception e) - { - Tracer.WriteMessage("PSWorkflowApplicationInstance", "DoLoadInstanceForReactivation", id, "There has been an exception while loading the workflow state from persistence store."); - Tracer.TraceException(e); - var invalidOpException = new InvalidOperationException(Resources.WorkflowInstanceIncompletelyPersisted, e); - HandleWorkflowApplicationFaultedState(invalidOpException); - throw invalidOpException; - } - } - - /// - /// PerformTaskAtTerminalState - /// - internal override void PerformTaskAtTerminalState() - { - - // Cleanup - if (this.PSWorkflowDefinition != null && this.PSWorkflowDefinition.Workflow != null) - { - this.PSWorkflowDefinition.Workflow = null; - } - - - if (this.CheckForStartOrEndPersistence()) - { - try - { - //Serializing Data to disk - _stores.Save(WorkflowStoreComponents.Metadata - | WorkflowStoreComponents.Streams - | WorkflowStoreComponents.TerminatingError - | WorkflowStoreComponents.Timer - | WorkflowStoreComponents.JobState - | WorkflowStoreComponents.ActivityState - ); - } - catch (Exception e) - { - Tracer.WriteMessage("Serialization exception occurred while saving workflow to persistence store"); - Tracer.TraceException(e); - // Trace details - if (Streams.ErrorStream != null && Streams.ErrorStream.IsOpen) - { - Streams.ErrorStream.Add(new ErrorRecord(e, "Workflow_Serialization_Error", ErrorCategory.ParserError, null)); - } - else - { - Tracer.WriteMessage("Error stream is not in Open state"); - } - } - } - - Streams.CloseAll(); - - // Stopping the timers - if (Timer != null) - { - Timer.StopTimer(WorkflowTimerType.RunningTimer); - } - - _structuredTracer.EndWorkflowExecution(_job.InstanceId); - } - - /// - /// Save streams if they are not already persisted - /// - /// false, if persistence was attempted and there - /// was an error - /// true - otherwise - internal override bool SaveStreamsIfNecessary() - { - // if there was start and end persistence, there is - // no need to persist again, simply return - if (CheckForStartOrEndPersistence()) - return true; - - try - { - _stores.Save(WorkflowStoreComponents.Streams); - } - catch (Exception e) - { - // it is fine to eat an exception here - // the user explicitly disabled persist and - // we are doing a best effort here - // any failure means we will simply return - // the caller will handle this scenario - Tracer.TraceException(e); - return false; - } - return true; - } - - #endregion Internal Members - - #region Public Members - - /// - /// PSWorkflowJob - /// - public override PSWorkflowJob PSWorkflowJob - { - get { return _job; } - - protected internal set - { - _job = value; - } - } - - /// - /// Gets the Guid of workflow instance. - /// - public override PSWorkflowId InstanceId - { - get { return new PSWorkflowId(this.id); } - } - - /// - /// Gets the Creation Context of workflow job. - /// - public override Dictionary CreationContext - { - get - { - Dictionary creationContext = null; - - object jobCreationContext = null; - if (this.PSWorkflowContext.JobMetadata.TryGetValue(Constants.WorkflowJobCreationContext, out jobCreationContext)) - { - creationContext = jobCreationContext as Dictionary; - } - - return creationContext; - } - } - - /// - /// InstanceStore - /// - public override PSWorkflowInstanceStore InstanceStore - { - get - { - return _stores; - } - } - - /// - /// Gets the definition of workflow. - /// - public override PSWorkflowDefinition PSWorkflowDefinition - { - get - { - if (this._definition == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - lock (SyncLock) - { - if (this._definition == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - try - { - _stores.Load(WorkflowStoreComponents.Definition); - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow definition"); - - Tracer.TraceException(e); - - this._definition = new PSWorkflowDefinition(null, string.Empty, string.Empty); - - - } - } - } - } - - return this._definition; - } - - set - { - CheckDisposed(); - this._definition = value; - } - } - - /// - /// Gets the streams of workflow. - /// - public override PowerShellStreams Streams - { - get - { - if (this._streams == null && - (_streamsDisposed || this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown)) - { - lock (SyncLock) - { - if (this._streams == null && - (_streamsDisposed || this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown)) - { - try - { - _stores.Load(WorkflowStoreComponents.Streams); - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow streams"); - - Tracer.TraceException(e); - - this._streams = new PowerShellStreams(null); - Tracer.WriteMessage("Marking the job to the faulted state."); - } - RegisterHandlersForDataAdding(_streams); - } - } - } - return this._streams; - } - - set - { - CheckDisposed(); - if (_streams == value) - return; - - if (_streams != null) - { - UnregisterHandlersForDataAdding(_streams); - _streams.Dispose(); - } - this._streams = value; - RegisterHandlersForDataAdding(_streams); - } - } - - /// - /// Gets the remote runspace instance ids collection. - /// - public override PSWorkflowRemoteActivityState RemoteActivityState - { - get - { - if (this._remoteActivityState == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - lock (SyncLock) - { - if (this._remoteActivityState == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - try - { - _stores.Load(WorkflowStoreComponents.ActivityState); - - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the RemoteActivityState"); - - Tracer.TraceException(e); - - this.RemoteActivityState = new PSWorkflowRemoteActivityState(_stores); - - Tracer.WriteMessage("Marking the job to the faulted state."); - } - } - } - } - - return this._remoteActivityState; - } - - set - { - CheckDisposed(); - this._remoteActivityState = value; - } - } - - /// - /// Gets the streams of workflow. - /// - public override Exception Error - { - get - { - if (this._errorException == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown && errorExceptionLoadCalled == false) - { - lock (SyncLock) - { - if (this._errorException == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown && errorExceptionLoadCalled == false) - { - try - { - errorExceptionLoadCalled = true; - _stores.Load(WorkflowStoreComponents.TerminatingError); - - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow terminating error"); - - Tracer.TraceException(e); - - this._errorException = null; - - Tracer.WriteMessage("Marking the job to the faulted state."); - } - } - } - } - - return this._errorException; - } - - set - { - CheckDisposed(); - this._errorException = value; - } - } - - /// - /// Gets the timers of workflow. - /// - public override PSWorkflowTimer Timer - { - get - { - if (this._timers == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - lock (SyncLock) - { - if (this._timers == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - try - { - _stores.Load(WorkflowStoreComponents.Timer); - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow timer"); - - Tracer.TraceException(e); - - this._timers = null; - - Tracer.WriteMessage("Marking the job to the faulted state."); - } - } - } - } - - return this._timers; - } - - set - { - CheckDisposed(); - this._timers = value; - } - } - - /// - /// Gets the metadatas of workflow. - /// - public override PSWorkflowContext PSWorkflowContext - { - get - { - if (this._metadatas == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - lock (SyncLock) - { - if (this._metadatas == null && this.creationMode == WorkflowInstanceCreationMode.AfterCrashOrShutdown) - { - try - { - _stores.Load(WorkflowStoreComponents.Metadata); - } - catch (Exception e) - { - Tracer.WriteMessage("Exception occurred while loading the workflow metadata"); - - Tracer.TraceException(e); - - this._metadatas = new PSWorkflowContext(); - - } - } - } - } - - return this._metadatas; - } - - set - { - CheckDisposed(); - this._metadatas = value; - } - - } - - /// - /// PSWorkflowDebugger - /// - internal override PSWorkflowDebugger PSWorkflowDebugger - { - get { return this._debugger; } - } - - /// - /// Dispose the streams to save memory - /// - public override void DisposeStreams() - { - if (_streams == null) return; - lock (SyncLock) - { - if (_streams == null) return; - _streamsDisposed = true; - UnregisterHandlersForDataAdding(_streams); - _streams.Dispose(); - _streams = null; - } - } - - private bool _streamsDisposed; - - #endregion Public Members - - #region Protected override members - - /// - /// DoStopInstance - /// - protected override void DoStopInstance() - { - // This lock required to wait for any inprogress workflow reactivation - lock (ReactivateSync) - { - // this flag ensures that the workflow application will not be loaded again since we are stopping the workflow instance - IsTerminalStateAction = true; - // this flag ensures that we don't unload the wfApplication instance since we are stopping the workflow. - PersistIdleTimerInProgressOrTriggered = false; - } - - if (this.workflowApplication != null) - { - this.workflowApplication.Cancel(); - return; - } - else - { - this.StopBookMarkedWorkflow(); - } - } - - /// - /// DoAbortInstance - /// - /// Reason for aborting workflow. - protected override void DoAbortInstance(string reason) - { - if (Disposed) - return; - - this.workflowApplication.Abort(reason); - } - - /// - /// DoTerminateInstance - /// - /// Reason message for termination - protected override void DoTerminateInstance(string reason) - { - // TimeoutException happens when workflowapplication can not be terminated in 30 sec. This happens on CTL VMs. - // if that timeout exception happens force stop job hangs on Finished event. - // Specifying timeout value to max value - this.workflowApplication.Terminate(reason, TimeSpan.MaxValue); - } - - /// - /// DoTerminateInstance - /// - /// Reason message for termination - /// Suppress error for termination - protected override void DoTerminateInstance(string reason, bool suppressError) - { - _suppressTerminateError = suppressError; - DoTerminateInstance(reason); - } - - /// - /// DoResumeInstance - /// - protected override void DoResumeInstance(string label) - { - Tracer.WriteMessage("Trying to resume workflow"); - - this.IsTerminalStateAction = false; - this.suspendAtNextCheckpoint = false; - - string prefix = string.IsNullOrEmpty(label) ? PSActivity.PSBookmarkPrefix : PSActivity.PSSuspendBookmarkPrefix + label; - - ReadOnlyCollection bookmarkInfos = this.workflowApplication.GetBookmarks(); - if (bookmarkInfos.Count > 0) - { - foreach (BookmarkInfo bookmarkInfo in bookmarkInfos) - { - if (bookmarkInfo.BookmarkName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - Bookmark bookmark = new Bookmark(bookmarkInfo.BookmarkName); - this.workflowApplication.ResumeBookmark(bookmark, ActivityOnResumeAction.Restart); - } - } - } - else - { - this.workflowApplication.Run(); - } - - this.StartTimerOnResume(); - Tracer.WriteMessage("Workflow resumed"); - } - - /// - /// DoSuspendInstance - /// - /// - protected override void DoSuspendInstance(bool notStarted) - { - if (notStarted && this.CheckForStartOrEndPersistence()) - { - this.workflowApplication.BeginUnload(OnSuspendUnloadComplete, null); - } - else - { - this.suspendAtNextCheckpoint = true; - } - } - - /// - /// DoExecuteInstance - /// - protected override void DoExecuteInstance() - { - if (Disposed) - return; - - Tracer.WriteMessage("Starting workflow execution"); - _structuredTracer.BeginWorkflowExecution(_job.InstanceId); - - try - { - this.workflowApplication.Run(); - } - catch (Exception e) - { - HandleWorkflowApplicationFaultedState(e); - return; - } - - this.StartTimers(); - Tracer.WriteMessage("Workflow application started execution"); - } - - /// - /// DoResumeBookmark - /// - /// - /// - protected override void DoResumeBookmark(Bookmark bookmark, object state) - { - if (bookmark == null) - throw new ArgumentNullException("bookmark"); - - ReactivateWorkflowInternal(bookmark, state, true); - } - - /// - /// Loads the xaml to create an executable activity. - /// - protected override void DoCreateInstance() - { - CheckDisposed(); - _structuredTracer.LoadingWorkflowForExecution(this.id); - lock (SyncLock) - { - Tracer.WriteMessage("Loading Workflow"); - - if (this.PSWorkflowDefinition.Workflow == null) - { - ArgumentException exception = new ArgumentException(Resources.NoWorkflowProvided); - Tracer.TraceException(exception); - throw exception; - } - - this.workflowApplication = this.PSWorkflowContext.WorkflowParameters == null - ? new WorkflowApplication(this.PSWorkflowDefinition.Workflow) - : new WorkflowApplication(this.PSWorkflowDefinition.Workflow, this.PSWorkflowContext.WorkflowParameters); - this.wfAppNeverLoaded = false; - - this.id = this.workflowApplication.Id; - SubscribeWorkflowApplicationEvents(); - - this.ConfigureAllExtensions(); - this.SetupTimers(); - - this.PersistBeforeExecution(); - - SetInternalUnloaded(false); - - Tracer.WriteMessage("Workflow is loaded, Guid = " + this.id.ToString("D", CultureInfo.CurrentCulture)); - } - _structuredTracer.WorkflowLoadedForExecution(this.id); - } - - /// - /// Remove - /// - protected override void DoRemoveInstance() - { - CheckDisposed(); - _stores.Delete(); - } - - /// - /// DoPersistInstance - /// - protected override void DoPersistInstance() - { - if (_job.JobStateInfo.State == JobState.Completed || _job.JobStateInfo.State == JobState.Stopped || _job.JobStateInfo.State == JobState.Failed) - { - // when job is reached to a terminal state so we just need to persist the required information - _stores.Save(WorkflowStoreComponents.Metadata - | WorkflowStoreComponents.Streams - | WorkflowStoreComponents.TerminatingError - | WorkflowStoreComponents.JobState - ); - } - else - { - // if job has not reached the terminal state then the persistence will happen after the completion of currently running activity. - this.PersistAfterNextPSActivity = true; - } - } - - /// - /// DoGetPersistableIdleAction - /// - /// - /// - /// - protected override PSPersistableIdleAction DoGetPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest) - { - if (bookmarks.Count == 0) - return PSPersistableIdleAction.None; - - Collection bks = new Collection(); - foreach (BookmarkInfo bk in bookmarks) - bks.Add(bk); - - // Check if this is a suspend request - if (VerifyRequest(bks, PSActivity.PSSuspendBookmarkPrefix)) - return PSPersistableIdleAction.Suspend; - - // Check if this is a persist request - if (VerifyRequest(bks, PSActivity.PSPersistBookmarkPrefix)) - { - if (externalSuspendRequest) - return PSPersistableIdleAction.Suspend; - - return PSPersistableIdleAction.Persist; - } - - if (bks.Count > 0) - return PSPersistableIdleAction.Unload; - - return PSPersistableIdleAction.None; - } - - // This will return true when all bookmarks are of type same prefix. (like suspend or persist) - // We may have branches (i.e.: in a parallel statement) where bookmarks are - // being used for other things. - private bool VerifyRequest(Collection bookmarks, string prefix) - { - if ((bookmarks == null) || (bookmarks.Count <= 0)) - return false; - - Collection toRemove = new Collection(); - bool rc = true; - foreach (BookmarkInfo bookmark in bookmarks) - { - if (!bookmark.BookmarkName.Contains(prefix)) - { - rc = false; - } - else - { - toRemove.Add(bookmark); - } - } - - foreach (BookmarkInfo r in toRemove) - { - bookmarks.Remove(r); - } - - return rc; - } - - - /// - /// Dispose - /// - protected override void Dispose(bool disposing) - { - if (!disposing || Disposed) - return; - - lock (SyncLock) - { - if (Disposed) - return; - - Disposed = true; - - lock (ReactivateSync) - { - this.IsTerminalStateAction = true; - this.PersistIdleTimerInProgressOrTriggered = false; - SetInternalUnloaded(false); - } - - this.ConfigureTimerOnUnload(); - - // saving the workflow application handle into the temporary variable - // then unregistering the workflow application handle - // temporary variable will be used to call abort if workflow is in running state - WorkflowApplication wf = this.workflowApplication; - DisposeWorkflowApplication(); - - if (this._job.JobStateInfo.State == JobState.Running && wf != null) - { - try - { - wf.Abort("Disposing the job"); - } - catch (Exception) - { - // We are not re-throwing any exception during Dispose - } - } - - if (_paramDefaults != null) - { - _paramDefaults.Dispose(); - _paramDefaults = null; - } - - if (_streams != null) - { - UnregisterHandlersForDataAdding(_streams); - _streams.Dispose(); - } - - if (_timers != null) - _timers.Dispose(); - - if (_debugger != null) - _debugger.Dispose(); - - DisposePersistUnloadTimer(); - - base.Dispose(disposing); - } - } - - #endregion Protected override members - - #region Private Members - - private void SetupTimers() - { - if (Timer != null && PSWorkflowContext.PSWorkflowCommonParameters != null) - { - // Running time - if (PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.PSRunningTime)) - { - int timeInSeconds = Convert.ToInt32(PSWorkflowContext.PSWorkflowCommonParameters[Constants.PSRunningTime], CultureInfo.CurrentCulture); - - if (timeInSeconds > 0) - { - Timer.SetupTimer(WorkflowTimerType.RunningTimer, TimeSpan.FromSeconds(timeInSeconds)); - } - } - - // Elapsed time - if (PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.PSElapsedTime)) - { - int timeInSeconds = Convert.ToInt32(PSWorkflowContext.PSWorkflowCommonParameters[Constants.PSElapsedTime], CultureInfo.CurrentCulture); - - if (timeInSeconds > 0) - { - Timer.SetupTimer(WorkflowTimerType.ElapsedTimer, TimeSpan.FromSeconds(timeInSeconds)); - } - } - - // Persist Interval - bool psPersistValue = true; - if (PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.Persist)) - { - psPersistValue = Convert.ToBoolean(PSWorkflowContext.PSWorkflowCommonParameters[Constants.Persist], CultureInfo.CurrentCulture); - } - } - } - - private HostParameterDefaults _paramDefaults; - private void ConfigureAllExtensions() - { - // declaring instance store - this.workflowApplication.InstanceStore = _stores.CreateInstanceStore(); - - var IOParticipant = _stores.CreatePersistenceIOParticipant(); - if (IOParticipant != null) - this.workflowApplication.Extensions.Add(IOParticipant); - - // adding the tracking participants - this.workflowApplication.Extensions.Add(this.GetTrackingParticipant()); - - // adding the custom extensions - IEnumerable extensions = this.Runtime.Configuration.CreateWorkflowExtensions(); - if (extensions != null) - { - foreach (object extension in extensions) - { - this.workflowApplication.Extensions.Add(extension); - } - } - - // adding the custom extension creation functions - IEnumerable> extensionFunctions = this.Runtime.Configuration.CreateWorkflowExtensionCreationFunctions(); - if (extensionFunctions != null) - { - foreach(Func extensionFunc in extensionFunctions) - { - this.workflowApplication.Extensions.Add(extensionFunc); - } - } - - _paramDefaults = new HostParameterDefaults(); - - if (this.PSWorkflowContext.PSWorkflowCommonParameters != null) - { - foreach (KeyValuePair param in this.PSWorkflowContext.PSWorkflowCommonParameters) - { - if (param.Key != Constants.PSRunningTime && param.Key != Constants.PSElapsedTime) - { - _paramDefaults.Parameters.Add(param.Key, param.Value); - } - } - } - - if (this.PSWorkflowContext.PrivateMetadata != null) - { - _paramDefaults.Parameters[Constants.PrivateMetadata] = this.PSWorkflowContext.PrivateMetadata; - } - - // Job related parameters - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataName)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataName)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataName]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataInstanceId)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataInstanceId)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataInstanceId]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataSessionId)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataSessionId)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataSessionId]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataCommand)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataCommand)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataCommand]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentName)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataParentName)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentName]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentInstanceId)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataParentInstanceId)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentInstanceId]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentSessionId)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataParentSessionId)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentSessionId]; - } - - if (this.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentCommand)) - { - _paramDefaults.Parameters[TranslateMetaDataName(Constants.JobMetadataParentCommand)] = - this.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentCommand]; - } - - _paramDefaults.Parameters["WorkflowInstanceId"] = this.InstanceId; - - _paramDefaults.Parameters["Input"] = this.Streams.InputStream; - _paramDefaults.Parameters["Result"] = this.Streams.OutputStream; - _paramDefaults.Parameters["PSError"] = this.Streams.ErrorStream; - _paramDefaults.Parameters["PSWarning"] = this.Streams.WarningStream; - _paramDefaults.Parameters["PSProgress"] = this.Streams.ProgressStream; - _paramDefaults.Parameters["PSVerbose"] = this.Streams.VerboseStream; - _paramDefaults.Parameters["PSDebug"] = this.Streams.DebugStream; - _paramDefaults.Parameters["PSInformation"] = this.Streams.InformationStream; - - // Assign PSActivityHost here - _paramDefaults.Runtime = Runtime; - - _paramDefaults.JobInstanceId = _job.InstanceId; - - // Assign PSHostPersistDelegate here - Func hostDelegate = this.CheckForPersistenceAfterPSActivity; - _paramDefaults.HostPersistenceDelegate = hostDelegate; - - Action activateDelegate = this.ReactivateWorkflow; - _paramDefaults.ActivateDelegate = activateDelegate; - - _paramDefaults.AsyncExecutionCollection = this.asyncExecutionCollection; - _paramDefaults.RemoteActivityState = this.RemoteActivityState; - - System.Activities.Hosting.SymbolResolver resolver = new System.Activities.Hosting.SymbolResolver(); - resolver.Add("ParameterDefaults", _paramDefaults); - - this.workflowApplication.Extensions.Add(resolver); - this.workflowApplication.Extensions.Add(_paramDefaults); - } - - - private void PersistBeforeExecution() - { - if (this.CheckForStartOrEndPersistence()) - { - try - { - // For robustness -// _structuredTracer.PersistingWorkflow(this.id, (_runtime.Configuration as PSWorkflowConfigurationProvider).InstanceStorePath); - this.workflowApplication.Persist(); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - } - } - - private void StartTimers() - { - if (Timer != null) - { - Timer.StartTimer(WorkflowTimerType.RunningTimer); - Timer.StartTimer(WorkflowTimerType.ElapsedTimer); - } - } - - private void ConfigureTimerOnUnload() - { - if (Timer != null) - { - Timer.StopTimer(WorkflowTimerType.RunningTimer); - } - } - - private void StartTimerOnResume() - { - - if (Timer != null) - { - Timer.StartTimer(WorkflowTimerType.RunningTimer); - } - } - - /// - /// Construct the workflow tracking participant. - /// - /// Returns the workflow tracking participant. - private PSWorkflowTrackingParticipant GetTrackingParticipant() - { - const String all = "*"; - PSWorkflowTrackingParticipant participant = new PSWorkflowTrackingParticipant(this._debugger) - { - // Create a tracking profile to subscribe for tracking records - // In this sample the profile subscribes for CustomTrackingRecords, - // workflow instance records and activity state records - TrackingProfile = new TrackingProfile() - { - Name = "WorkflowTrackingProfile", - Queries = - { - new CustomTrackingQuery() - { - Name = all, - ActivityName = all - }, - new WorkflowInstanceQuery() - { - // Limit workflow instance tracking records for started and completed workflow states - States = { - WorkflowInstanceStates.Started, - WorkflowInstanceStates.Completed, - WorkflowInstanceStates.Persisted, - WorkflowInstanceStates.UnhandledException - }, - }, - new ActivityStateQuery() - { - // Subscribe for track records from all activities for all states - ActivityName = all, - States = { all }, - - // Extract workflow variables and arguments as a part of the activity tracking record - // VariableName = "*" allows for extraction of all variables in the scope - // of the activity - Variables = - { - { all } - }, - - Arguments = - { - { all } - } - } - } - } - }; - - return participant; - } - - private bool CheckForStartOrEndPersistence() - { - // This is for the unit testing - // Inside the unit test the force disable attribute can be set to true. - if (ForceDisableStartOrEndPersistence == true) - return false; - - if (this.PSWorkflowContext != null && this.PSWorkflowContext.PSWorkflowCommonParameters != null && this.PSWorkflowContext.PSWorkflowCommonParameters.ContainsKey(Constants.Persist)) - { - bool? value = this.PSWorkflowContext.PSWorkflowCommonParameters[Constants.Persist] as bool?; - if (value != null && value == false) - return false; - } - - return true; - } - - private bool CheckForPersistenceAfterPSActivity() - { - bool value = this.PersistAfterNextPSActivity; - this.PersistAfterNextPSActivity = false; - - return value; - } - - #endregion Private Members - - #region Persist and Reactivation - - internal bool InternalUnloaded; - - private void SetInternalUnloaded(bool value) - { - InternalUnloaded = value; - - if (this.RemoteActivityState != null) - this.RemoteActivityState.InternalUnloaded = value; - } - - private bool wfAppNeverLoaded = true; - - private bool PersistIdleTimerInProgressOrTriggered = false; - private bool IsTerminalStateAction = false; - - private Timer PersistUnloadTimer; - private object ReactivateSync = new object(); - - private void ReactivateWorkflow(object state) - { - Debug.Assert(state != null, "State not passed correctly to ReactivateWorkflow"); - Bookmark bookmark = state as Bookmark; - Debug.Assert(bookmark != null, "Bookmark not passed correctly to ReactivateWorkflow"); - - ReactivateWorkflowInternal(bookmark, null, false); - } - - private void ReactivateWorkflowInternal(Bookmark bookmark, object state, bool validateBookmark) - { - if (Disposed || IsTerminalStateAction) - return; - - CheckAndLoadInstanceForReactivation(); - - Debug.Assert(this.workflowApplication != null, "PSWorkflowApplicationInstance is not initialized properly"); - if (this.workflowApplication == null) - return; - - if (validateBookmark && this.CheckIfBookmarkExistInCollection(bookmark.Name, this.workflowApplication.GetBookmarks()) == false) - { - Tracer.WriteMessage("PSWorkflowInstance", "ReactivateWorkflowInternal", this.id,"Invalid bookmark: '{0}'.", bookmark.Name); - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidBookmark,bookmark.Name)); - } - - try - { - this.workflowApplication.ResumeBookmark(bookmark, state); - } - catch (Exception e) - { - // there should not be any exception if any then writing to the logs - Tracer.TraceException(e); - HandleWorkflowApplicationFaultedState(e); - } - } - - private void CheckAndLoadInstanceForReactivation() - { - if (Disposed || IsTerminalStateAction || (PersistIdleTimerInProgressOrTriggered == false && this.wfAppNeverLoaded == false)) - return; - - lock (ReactivateSync) - { - if (Disposed || IsTerminalStateAction || (PersistIdleTimerInProgressOrTriggered == false && this.wfAppNeverLoaded == false)) - return; - - if (this.InternalUnloaded || this.wfAppNeverLoaded) - { - this.DoLoadInstanceForReactivation(); - SetInternalUnloaded(false); - } - - PersistIdleTimerInProgressOrTriggered = false; - } - } - - private bool CheckIfBookmarkExistInCollection(string bookmarkName, ReadOnlyCollection bookmarks) - { - foreach (BookmarkInfo bookmark in bookmarks) - { - if (bookmarkName == bookmark.BookmarkName) - return true; - } - - return false; - } - - private void StopBookMarkedWorkflow() - { - // Workflow is currently in booked marked state - // we will try to cancel all async operations - // and then perform the terminal tasks related to stop workflow - - if (Disposed) - return; - - if (System.Threading.Interlocked.CompareExchange(ref terminalStateHandled, Handled, NotHandled) == Handled) - return; - - lock (ReactivateSync) - { - if (Disposed) - return; - - IsTerminalStateAction = true; - - StopAllAsyncExecutions(); - - Tracer.WriteMessage("Workflow is in Canceled state."); - - State = JobState.Stopped; - this.PerformTaskAtTerminalState(); - - // do all cleanups here to save memory - PerformCleanupAtTerminalState(); - - if (this.OnStopped != null) - this.OnStopped(this); - } - } - - private void StopAllAsyncExecutions() - { - if (Disposed) - return; - - if (this.asyncExecutionCollection != null && this.asyncExecutionCollection.Count > 0) - { - // This is not a full fix: - // there could be a race condition where we have taken the copy but context is removed from by activity and disposed in that case contextinstance.cancel will throw - // there could be one more possibility that is if copy has been made and after that activity adds a new context and will not get canceled and streams will get closed so async execution might throw - // the solution is make every these operation on async execution collection thread safe - - foreach (PSActivityContext psActivityContextInstance in this.asyncExecutionCollection.Values.ToList()) - { - if (psActivityContextInstance != null) - { - psActivityContextInstance.IsCanceled = true; - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "PowerShell activity: Executing cancel request.")); - - psActivityContextInstance.Cancel(); - } - } - - this.asyncExecutionCollection.Clear(); - } - } - - private void StartPersistUnloadWithZeroSeconds() - { - lock (ReactivateSync) - { - if (IsTerminalStateAction) - return; - - PersistIdleTimerInProgressOrTriggered = true; - SetInternalUnloaded(true); - } - } - - private void StartPersistUnloadTimer(int delaySeconds) - { - // return if persist idle is in progress - if (PersistIdleTimerInProgressOrTriggered) - return; - - lock (ReactivateSync) - { - // return if persist idle is in progress - if (PersistIdleTimerInProgressOrTriggered) - return; - - if (IsTerminalStateAction) - return; - - PersistIdleTimerInProgressOrTriggered = true; - PersistUnloadTimer.Enabled = true; - } - } - - private void DisposePersistUnloadTimer() - { - if (PersistUnloadTimer == null) - return; - - lock (ReactivateSync) - { - if (PersistUnloadTimer == null) - return; - - PersistUnloadTimer.Elapsed -= new ElapsedEventHandler(PersistUnloadTimer_Elapsed); - PersistUnloadTimer.Dispose(); - PersistUnloadTimer = null; - } - } - - private void PersistUnloadTimer_Elapsed(object sender, ElapsedEventArgs e) - { - if (PersistIdleTimerInProgressOrTriggered == false || IsTerminalStateAction) - return; - - lock (ReactivateSync) - { - if (IsTerminalStateAction) - return; - - if (PersistIdleTimerInProgressOrTriggered) - { - SetInternalUnloaded(true); - - // Check for the race condition. - // There could be possibility workflow application is unloaded because of terminal state is reached. - if (this.workflowApplication != null) - { - try - { - this.OnUnloadComplete(this.workflowApplication.BeginUnload(null, null)); - } - catch (Exception exp) - { - Tracer.WriteMessage("PSWorkflowInstance", "PersistUnloadTimer_Elapsed", id, "Got an exception while unloading the workflow Application."); - Tracer.TraceException(exp); - return; - } - } - } - } - } - private void OnUnloadComplete(IAsyncResult result) - { - if (Disposed) return; - - try - { - this.workflowApplication.EndUnload(result); - } - catch (Exception e) - { - Tracer.WriteMessage("PSWorkflowInstance", "PersistUnloadTimer_Elapsed", id, "Not able to unload workflow application in a given timeout."); - Tracer.TraceException(e); - return; - } - - this.DisposeWorkflowApplication(); - } - - /// - /// CheckForTerminalAction - /// - internal override void CheckForTerminalAction() - { - lock (ReactivateSync) - { - IsTerminalStateAction = true; - PersistIdleTimerInProgressOrTriggered = false; - if (this.InternalUnloaded) - { - this.DoLoadInstanceForReactivation(); - SetInternalUnloaded(false); - } - } - } - - /// - /// Validate if the label exists. - /// - /// - internal override void ValidateIfLabelExists(string label) - { - string prefix = PSActivity.PSSuspendBookmarkPrefix + label; - - ReadOnlyCollection bookmarkInfos = this.workflowApplication.GetBookmarks(); - - bool found = false; - foreach (BookmarkInfo bookmarkInfo in bookmarkInfos) - { - if (bookmarkInfo.BookmarkName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - found = true; - } - } - - if (!found) - { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidLabel, label)); - } - } - - #endregion - - #region Static Methods - - /// - /// Helper method to translate internal Workflow metadata names to - /// public Workflow variable names. - /// - /// Metadata key string - /// Public workflow variable name - internal static string TranslateMetaDataName(string metaDataName) - { - string rtnName; - - switch (metaDataName) - { - case Constants.JobMetadataName: - rtnName = "JobName"; - break; - - case Constants.JobMetadataInstanceId: - rtnName = "JobInstanceId"; - break; - - case Constants.JobMetadataSessionId: - rtnName = "JobId"; - break; - - case Constants.JobMetadataCommand: - rtnName = "JobCommandName"; - break; - - case Constants.JobMetadataParentName: - rtnName = "ParentJobName"; - break; - - case Constants.JobMetadataParentInstanceId: - rtnName = "ParentJobInstanceId"; - break; - - case Constants.JobMetadataParentSessionId: - rtnName = "ParentJobId"; - break; - - case Constants.JobMetadataParentCommand: - rtnName = "ParentCommandName"; - break; - - case Constants.JobMetadataLocation: - rtnName = "PSComputerName"; - break; - - default: - rtnName = metaDataName; - break; - } - - return rtnName; - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJob2.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJob2.cs deleted file mode 100644 index 9ca89c5d6a8..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJob2.cs +++ /dev/null @@ -1,2205 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Linq; -using System.Activities; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Tracing; -using System.Management.Automation.PerformanceData; -using System.Threading; -using Dbg = System.Diagnostics.Debug; -using System.Activities.Statements; -using System.Collections.ObjectModel; -using System.Activities.Hosting; -using Microsoft.PowerShell.Activities; - -// Stops compiler from warning about unknown warnings -#pragma warning disable 1634, 1691 - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// Workflow Job type implementation. For use with the WorkflowJobSourceAdapter. - /// - public class PSWorkflowJob : Job2, IJobDebugger - { - #region Public Accessors - - /// - /// Delegate action on workflow idling - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is needed for taking the decision for evicting the workflows.")] - public Action> OnIdle { get; set; } - - /// - /// Delegate function on workflow persist idle action - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is needed for taking the decision for evicting the workflows.")] - public Func, bool, PSPersistableIdleAction> OnPersistableIdleAction { get; set; } - - /// - /// Delegate action on workflow unloaded - /// - public Action OnUnloaded { get; set; } - - /// - /// Signaled when job reaches running state - /// - internal WaitHandle Running { get { return this.JobRunning; } } - - /// - /// Signaled when job finishes suspending or aborting - /// - internal WaitHandle SuspendedOrAborted { get { return this.JobSuspendedOrAborted; } } - - /// - /// Job input parameters - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - internal Dictionary WorkflowParameters { get { return _workflowParameters; } } - - /// - /// Job input parameters including system provided parameters - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - internal Dictionary PSWorkflowCommonParameters { get { return _psWorkflowCommonParameters; } } - - /// - /// Job metadata collection - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - internal Dictionary JobMetadata { get { return _jobMetadata; } set { _jobMetadata = value; } } - - /// - /// Job invoker metadata collection - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - internal Dictionary PrivateMetadata { get { return _privateMetadata; } } - - /// - /// Workflow instance for the job - /// - public PSWorkflowInstance PSWorkflowInstance - { - get { return _workflowInstance; } - internal set { _workflowInstance = value; } - } - - /// - /// Workflow debugger - /// - public Debugger PSWorkflowDebugger - { - get - { - return (_workflowInstance != null) ? _workflowInstance.PSWorkflowDebugger : null; - } - } - - /// - /// Workflow job definition - /// - internal WorkflowJobDefinition PSWorkflowJobDefinition - { - get { return _definition; } - } - - internal Dictionary JobCreationContext - { - get { return _jobCreationContext; } - } - - #endregion - - #region Private Members - - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private const string ClassNameTrace = "PSWorkflowJob"; - private static readonly Tracer StructuredTracer = new Tracer(); - private readonly object _syncObject = new object(); - private readonly object _resumeErrorSyncObject = new object(); - private bool _isDisposed; - private string _statusMessage = string.Empty; - private string _location = string.Empty; - private readonly WorkflowJobDefinition _definition; - private readonly PSWorkflowRuntime _runtime; - private JobState _previousState; - private PSWorkflowInstance _workflowInstance; - private static readonly PSPerfCountersMgr _perfCountersMgr = PSPerfCountersMgr.Instance; - private readonly Dictionary _resumeErrors = new Dictionary(); - private readonly Dictionary _jobCreationContext; - - /// - /// Input parameters to the workflow instance. - /// - - private Dictionary _workflowParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _psWorkflowCommonParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _jobMetadata = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _privateMetadata = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // Holds the collection of input objects received from the pipeline - private PSDataCollection _inputCollection; - - private ManualResetEvent _jobRunning; - private ManualResetEvent JobRunning - { - get - { - if (_jobRunning == null) - { - lock (_syncObject) - { - if (_jobRunning == null) - { - // this assert is required so that a wait handle - // is not created after the object is disposed - // which will result in a leak - AssertNotDisposed(); - _jobRunning = new ManualResetEvent(false); - } - } - } - - return _jobRunning; - } - } - - private ManualResetEvent _jobSuspendedOrAborted; - private ManualResetEvent JobSuspendedOrAborted - { - get - { - if (_jobSuspendedOrAborted == null) - { - lock (_syncObject) - { - if (_jobSuspendedOrAborted == null) - { - // this assert is required so that a wait handle - // is not created after the object is disposed - // which will result in a leak - AssertNotDisposed(); - _jobSuspendedOrAborted = new ManualResetEvent(false); - } - } - } - return _jobSuspendedOrAborted; - } - } - - private bool _isAsync = true; - - #endregion Private Members - - #region Private Methods - - /// - /// Provide validation of constructor parameter that could cause NullReferenceException. - /// - /// JobInvocationInfo for construction. - /// specification parameter if not null. - private static JobInvocationInfo Validate(JobInvocationInfo specification) - { - if (specification == null) - { - throw new ArgumentNullException("specification"); - } - - if (specification.Definition == null) - { - throw new ArgumentException(Resources.UninitializedSpecification, "specification"); - } - - if (string.IsNullOrEmpty(specification.Definition.Command)) - { - throw new ArgumentException(Resources.UninitializedSpecification, "specification"); - } - - return specification; - } - - private void InitializeWithWorkflow(PSWorkflowInstance instance, bool closeStreams = false) - { - Dbg.Assert(instance.Streams != null, "Workflow Instance has no stream data."); - _tracer.WriteMessage(ClassNameTrace, "InitializeWithWorkflow", WorkflowGuidForTraces, this, "Setting streams"); - Output = instance.Streams.OutputStream ?? new PSDataCollection(); - Progress = instance.Streams.ProgressStream ?? new PSDataCollection(); - Warning = instance.Streams.WarningStream ?? new PSDataCollection(); - Error = instance.Streams.ErrorStream ?? new PSDataCollection(); - Debug = instance.Streams.DebugStream ?? new PSDataCollection(); - Verbose = instance.Streams.VerboseStream ?? new PSDataCollection(); - Information = instance.Streams.InformationStream ?? new PSDataCollection(); - - if (!closeStreams) return; - - Output.Complete(); - Progress.Complete(); - Warning.Complete(); - Error.Complete(); - Debug.Complete(); - Verbose.Complete(); - Information.Complete(); - } - - private bool _starting; - private void DoStartJobLogic(object state) - { - if (_isDisposed) return; - Dbg.Assert(state == null, "State is never used"); - _tracer.WriteMessage(ClassNameTrace, "DoStartJobLogic", WorkflowGuidForTraces, this, "BEGIN"); - StructuredTracer.BeginJobLogic(InstanceId); - lock (SyncRoot) - { - AssertValidState(JobState.NotStarted); - if (_starting || _suspending || _resuming) return; - _starting = true; - } - - // Do Not set job state from within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Running); - - Dbg.Assert(_workflowInstance != null, "PSWorkflowInstance should have been populated by the adapter"); - Dbg.Assert(_workflowInstance.Id != Guid.Empty, "Workflow has not been loaded before StartJob"); - - _tracer.WriteMessage(ClassNameTrace, "DoStartJobLogic", WorkflowGuidForTraces, this, "ready to start"); - - _workflowInstance.ExecuteInstance(); - - // This message could appear after completion traces in some cases. - StructuredTracer.WorkflowExecutionStarted(_workflowInstance.Id, string.Empty); - _tracer.WriteMessage(ClassNameTrace, "DoStartJobLogic", WorkflowGuidForTraces, this, "END"); - } - - private bool _stopCalled; - private void DoStopJob() - { - if (_isDisposed || IsFinishedState(JobStateInfo.State) || JobState.Stopping == JobStateInfo.State || _stopCalled) return; - - bool cancel = false; - bool waitForRunning = false; - bool waitForSuspendorAbort = false; - - lock (SyncRoot) - { - if (_isDisposed || IsFinishedState(JobStateInfo.State) || JobState.Stopping == JobStateInfo.State || _stopCalled) return; - - if (_suspending) - waitForSuspendorAbort = true; - else if (_starting || _resuming) - waitForRunning = true; - - _tracer.WriteMessage(ClassNameTrace, "DoStopJob", WorkflowGuidForTraces, this, "BEGIN"); - - _stopCalled = true; - - if (JobStateInfo.State == JobState.Running) - { - cancel = true; - } - } - - // actual stop logic goes here - if (cancel) - { - if (waitForSuspendorAbort) - JobSuspendedOrAborted.WaitOne(); - else if (waitForRunning) - JobRunning.WaitOne(); - - // Do not set job state from within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Stopping); - - StructuredTracer.CancellingWorkflowExecution(_workflowInstance.Id); - _workflowInstance.StopInstance(); - } - else - { - // Do not set job state from within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Stopping); - - _workflowInstance.State = JobState.Stopped; - _workflowInstance.PerformTaskAtTerminalState(); - DoSetJobState(JobState.Stopped); - } - StructuredTracer.WorkflowExecutionCancelled(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - - _tracer.WriteMessage(ClassNameTrace, "DoStopJob", WorkflowGuidForTraces, this, "END"); - } - - private bool _suspending = false; - - /// - /// DoSuspendJob returns a bool, but - /// in the case of a SuspendJob call, we do not - /// want to return until the job is in the correct state. - /// The boolean return value is used by the WorkflowManager - /// for Shutdown purposes. - /// - /// - internal bool DoSuspendJob() - { - bool waitNeeded = true; - - if (_isDisposed) - { - return waitNeeded; - } - - bool waitForRunning = false; - bool notStarted = false; - - lock (SyncRoot) - { - if (_isDisposed || JobStateInfo.State == JobState.Suspending || JobStateInfo.State == JobState.Suspended || - _suspending) - { - return waitNeeded; - } - - if (this.IsSuspendable != null && IsSuspendable.HasValue && IsSuspendable.Value == false) - { - _tracer.WriteMessage(ClassNameTrace, "DoSuspendJob", WorkflowGuidForTraces, this, - "The job is not suspendable."); - throw new InvalidOperationException(Resources.ErrorMessageForPersistence); - } - - if (_starting) - waitForRunning = true; - - _tracer.WriteMessage(ClassNameTrace, "DoSuspendJob", WorkflowGuidForTraces, this, "BEGIN"); - _suspending = true; - - if (JobStateInfo.State != JobState.Running && JobStateInfo.State != JobState.NotStarted) - { - _tracer.WriteMessage(ClassNameTrace, "DoSuspendJob", WorkflowGuidForTraces, this, "InvalidJobState"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.SuspendNotValidState); - } - if (JobStateInfo.State == JobState.NotStarted) - { - notStarted = true; - } - } - - if (waitForRunning) - JobRunning.WaitOne(); - - // Do not set job state from within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Suspending); - - lock (SyncRoot) - { - _resuming = false; - } - - _workflowInstance.SuspendInstance(notStarted); - - _tracer.WriteMessage(ClassNameTrace, "DoSuspendJob", WorkflowGuidForTraces, this, "END"); - return waitNeeded; - } - - /// - /// Do Set Job State - /// - /// - /// - /// returns false if state transition is not possible. Return value is required to update SQM perf counters - private bool DoSetJobState(JobState state, Exception reason = null) - { - if (IsFinishedState(_previousState) || _isDisposed) return false; - - lock (_syncObject) - { - if (IsFinishedState(_previousState) || _isDisposed) return false; - - // State should not be transitioned to suspended from stopping - if (_previousState == JobState.Stopping && state == JobState.Suspended) return false; - - // State should not be transitioned to suspended from suspended - if (_previousState == JobState.Suspended && state == JobState.Suspended) return false; - - if (state != _previousState && StructuredTracer.IsEnabled) - { - StructuredTracer.JobStateChanged(Id, InstanceId, state.ToString(), _previousState.ToString()); - } - -#if DEBUG - switch (_previousState) - { - case JobState.Running: - Dbg.Assert(state != JobState.NotStarted && state != JobState.Blocked, - "WorkflowJob invalid state transition from Running."); - break; - case JobState.Stopping: - //Dbg.Assert( - // state == JobState.Stopped || state == JobState.Failed || state == JobState.Completed, - // "WorkflowJob invalid state transition from Stopping."); - break; - case JobState.Stopped: - Dbg.Assert(false, "WorkflowJob should never transition after Stopped state."); - break; - case JobState.Suspending: - Dbg.Assert( - state == JobState.Suspended || state == JobState.Completed || state == JobState.Failed || - state == JobState.Stopped || state == JobState.Stopping, - "WorkflowJob invalid state transition from Suspending."); - break; - case JobState.Suspended: - Dbg.Assert( - state == JobState.Running || state == JobState.Stopping || state == JobState.Stopped || - state == JobState.Completed || state == JobState.Failed, - "WorkflowJob invalid state transition from Suspended."); - break; - case JobState.Failed: - Dbg.Assert(false, "WorkflowJob should never transition after Failed state."); - break; - case JobState.Completed: - Dbg.Assert(false, "WorkflowJob should never transition after Completed state."); - break; - case JobState.Disconnected: - Dbg.Assert(false, "WorkflowJob should never be in a disconnected state"); - break; - case JobState.Blocked: - Dbg.Assert(false, "WorkflowJob should never be in a blocked state"); - break; - default: - break; - } -#endif - - _previousState = state; - // Update JobMetadata - if (_workflowInstance != null) - { - _workflowInstance.PSWorkflowContext.JobMetadata.Remove(Constants.JobMetadataStateReason); - if (reason != null) - { - if (StructuredTracer.IsEnabled) - { - StructuredTracer.JobError(Id, InstanceId, Tracer.GetExceptionString(reason)); - } - _workflowInstance.PSWorkflowContext.JobMetadata.Add(Constants.JobMetadataStateReason, reason); - } - } - } - - _tracer.WriteMessage(ClassNameTrace, "DoSetJobState", WorkflowGuidForTraces, this, "Setting state to {0}, Setting Reason to exception: {1}", state.ToString(), reason == null ? null : reason.ToString()); - _workflowInstance.State = state; - - SetJobState(state, reason); - _tracer.WriteMessage(ClassNameTrace, "DoSetJobState", WorkflowGuidForTraces, this, "Done setting state"); - return true; - } - - /// - /// Create necessary dictionaries for WorkflowManager consumption based on StartParameters. - /// - private void SortStartParameters(DynamicActivity dynamicActivity, CommandParameterCollection parameters) - { - bool selfRemoting = dynamicActivity != null && dynamicActivity.Properties.Any(x => x.Name.Equals(Constants.ComputerName, StringComparison.CurrentCultureIgnoreCase)); - bool takesPSPrivateMetadata = dynamicActivity != null && dynamicActivity.Properties.Contains(Constants.PrivateMetadata); - _jobMetadata.Add(Constants.WorkflowTakesPrivateMetadata, takesPSPrivateMetadata); - - if (parameters != null) - { - foreach (var parameter in parameters) - { - _tracer.WriteMessage(ClassNameTrace, "SortStartParameters", WorkflowGuidForTraces, this, "Found parameter; {0}; {1}", parameter.Name, - parameter.Value == null ? null : parameter.Value.ToString()); - switch (parameter.Name) - { - case Constants.ComputerName: - if (selfRemoting) - { - // If we're self-remoting, location becomes the default computer - // and the PSComputerNames is passed in as an argument instead - // of going to the ubiquitous parameters - _location = Constants.DefaultComputerName; - string parameterName = dynamicActivity.Properties.First(x => x.Name.Equals(Constants.ComputerName, StringComparison.CurrentCultureIgnoreCase)).Name; - _workflowParameters[parameterName] = LanguagePrimitives.ConvertTo(parameter.Value); - } - else - { - // Set _location before adding parameter. - var computer = parameter.Value as string; - _location = computer; - string[] computerNames = LanguagePrimitives.ConvertTo(parameter.Value); - _psWorkflowCommonParameters[parameter.Name] = computerNames; - } - break; - case Constants.PrivateMetadata: - Hashtable privateData = parameter.Value as Hashtable; - if (privateData != null) - { - IDictionaryEnumerator enumerator = privateData.GetEnumerator(); - while (enumerator.MoveNext()) - { - _privateMetadata.Add(enumerator.Key.ToString(), enumerator.Value); - } - - // Make the combined object available within the workflow as well... - if (takesPSPrivateMetadata) - { - _workflowParameters.Add(parameter.Name, parameter.Value); - } - } - break; - - case Constants.PSInputCollection: - { - // Remove the input collection so we can properly pass it to the workflow job - object baseObject = parameter.Value is PSObject - ? ((PSObject)parameter.Value).BaseObject - : parameter.Value; - - if (baseObject is PSDataCollection) - { - _inputCollection = baseObject as PSDataCollection; - } - else - { - var inputCollection = new PSDataCollection(); - var e = LanguagePrimitives.GetEnumerator(baseObject); - if (e != null) - { - while (e.MoveNext()) - { - inputCollection.Add(PSObject.AsPSObject(e.Current)); - } - } - else - { - inputCollection.Add(PSObject.AsPSObject(parameter.Value)); - } - _inputCollection = inputCollection; - } - } - break; - - case Constants.PSParameterCollection: - // Remove this one from the parameter collection... - break; - case Constants.PSRunningTime: - case Constants.PSElapsedTime: - case Constants.ConnectionRetryCount: - case Constants.ActionRetryCount: - case Constants.ConnectionRetryIntervalSec: - case Constants.ActionRetryIntervalSec: - _psWorkflowCommonParameters.Add(parameter.Name, parameter.Value); - break; - - case Constants.Persist: - case Constants.Credential: - case Constants.Port: - case Constants.UseSSL: - case Constants.ConfigurationName: - case Constants.ApplicationName: - case Constants.ConnectionURI: - case Constants.SessionOption: - case Constants.Authentication: - case Constants.AuthenticationLevel: - case Constants.CertificateThumbprint: - case Constants.AllowRedirection: - case Constants.Verbose: - case Constants.Debug: - case Constants.ErrorAction: - case Constants.WarningAction: - case Constants.InformationAction: - case Constants.PSWorkflowErrorAction: - case Constants.PSSuspendOnError: - case Constants.PSSenderInfo: - case Constants.ModulePath: - case Constants.PSCurrentDirectory: - // Note: We don't add ErrorVariable, WarningVariable, OutVariable, or OutBuffer - // here because they are interpreted by PowerShell in the function generated over - // the workflow definition. - _psWorkflowCommonParameters.Add(parameter.Name, parameter.Value); - break; - default: - _workflowParameters.Add(parameter.Name, parameter.Value); - break; - } - } - } - - // Add in the workflow command name... - _psWorkflowCommonParameters.Add("WorkflowCommandName", _definition.Command); - } - - - private void HandleMyStateChanged(object sender, JobStateEventArgs e) - { - _tracer.WriteMessage(ClassNameTrace, "HandleMyStateChanged", WorkflowGuidForTraces, this, - "NewState: {0}; OldState: {1}", e.JobStateInfo.State.ToString(), - e.PreviousJobStateInfo.State.ToString()); - bool unloadStreams = false; - if (e.PreviousJobStateInfo.State == JobState.NotStarted) - { - PSBeginTime = DateTime.Now; - } - - switch (e.JobStateInfo.State) - { - case JobState.Running: - { - lock (SyncRoot) - { - _suspending = false; - _resuming = false; - wfSuspendInProgress = false; - } - - lock (_syncObject) - { - JobRunning.Set(); - - // Do not create the event if it doesn't already exist. Suspend may never be called. - if (_jobSuspendedOrAborted != null) - JobSuspendedOrAborted.Reset(); - - // Clear the message indicating that the job was suspended or that the job was queued - // for resume. The job is now running, and that status is no longer valid. - _statusMessage = String.Empty; - } - } - break; - - case JobState.Suspended: - { - lock (SyncRoot) - { - _suspending = false; - _resuming = false; - wfSuspendInProgress = false; - } - - PSEndTime = DateTime.Now; - lock (_syncObject) - { - JobSuspendedOrAborted.Set(); - JobRunning.Reset(); - } - unloadStreams = true; - } - break; - case JobState.Failed: - case JobState.Completed: - case JobState.Stopped: - { - PSEndTime = DateTime.Now; - - lock (_syncObject) - { - StructuredTracer.EndJobLogic(InstanceId); - JobSuspendedOrAborted.Set(); - - // Do not reset JobRunning when the state is terminal. - // No thread should wait on a job transitioning again to - // JobState.Running. - JobRunning.Set(); - } - unloadStreams = true; - } - break; - } - - if (!unloadStreams || !_unloadStreamsOnPersistentState) return; - - _tracer.WriteMessage(ClassNameTrace, "HandleMyStateChanged", WorkflowGuidForTraces, this, - "BEGIN Unloading streams from memory"); - SelfUnloadJobStreams(); - _tracer.WriteMessage(ClassNameTrace, "HandleMyStateChanged", WorkflowGuidForTraces, this, - "END Unloading streams from memory"); - } - - private void DoStartJobAsync(object state) - { - _tracer.WriteMessage(ClassNameTrace, "DoStartJobAsync", WorkflowGuidForTraces, this, ""); - Dbg.Assert(state == null, "State is never used"); - var asyncOp = AsyncOperationManager.CreateOperation(null); - - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - workerDelegate.BeginInvoke( - asyncOp, - ActionType.Start, - string.Empty, - string.Empty, - false, - null, - null); - } - - private void AssertValidState(JobState expectedState) - { - AssertNotDisposed(); - lock (SyncRoot) - { - if (JobStateInfo.State != expectedState) - { - throw new InvalidJobStateException(JobStateInfo.State, Resources.JobCannotBeStarted); - } - } - } - - private bool _resuming; - private void DoResumeJob(object state) - { - if (_isDisposed) return; - var tuple = state as Tuple; - string label = tuple != null ? tuple.Item1 ?? String.Empty : String.Empty; - - if (string.IsNullOrEmpty(label) == false) - { - DoLabeledResumeJob(label); - return; - } - - lock (SyncRoot) - { - if (_isDisposed || JobStateInfo.State == JobState.Running || _resuming) return; - _tracer.WriteMessage(ClassNameTrace, "DoResumeJob", WorkflowGuidForTraces, this, "BEGIN"); - - if (JobStateInfo.State != JobState.Suspended) - { - _tracer.WriteMessage(ClassNameTrace, "DoResumeJob", WorkflowGuidForTraces, this, - "InvalidJobState"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.ResumeNotValidState); - } - - // this will avoid the race condition between two resume requests and both are trying to load instance and loadstreams. - _resuming = true; - } - - _workflowInstance.DoLoadInstanceForReactivation(); - - // load the streams before resuming - LoadJobStreams(); - - // Do not set state within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Running); - - // actual logic of resuming a job - StructuredTracer.WorkflowResuming(_workflowInstance.Id); - _workflowInstance.ResumeInstance(label); - - lock (SyncRoot) - { - // once the resume instance is called then we can allow the subsequent suspend requests - _suspending = false; - wfSuspendInProgress = false; - - } - - StructuredTracer.WorkflowResumed(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "DoResumeJob", WorkflowGuidForTraces, this, "END"); - } - - private void DoLabeledResumeJob(string label) - { - bool waitNeeded = false; - - lock (SyncRoot) - { - if (_isDisposed) return; - _tracer.WriteMessage(ClassNameTrace, "DoLabeledResumeJob", WorkflowGuidForTraces, this, "BEGIN"); - - if (wfSuspendInProgress) - { - waitNeeded = true; - } - - listOfLabels.Add(label); - } - - try - { - if (waitNeeded) - JobSuspendedOrAborted.WaitOne(); - - lock (SyncRoot) - { - wfSuspendInProgress = false; - - if (JobStateInfo.State != JobState.Suspended && JobStateInfo.State != JobState.Running) - { - _tracer.WriteMessage(ClassNameTrace, "DoLabeledResumeJob", WorkflowGuidForTraces, this, "InvalidJobState"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.ResumeNotValidState); - } - - if (JobStateInfo.State != JobState.Running && !_resuming) - _workflowInstance.DoLoadInstanceForReactivation(); - - // this needs to be done here because this call may throw - // invalid bookmark exception, which should be happened before - // setting the job state to running. - _workflowInstance.ValidateIfLabelExists(label); - - _resuming = true; - } - - // load the streams before resuming - LoadJobStreams(); - - // Do not set state within the lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Running); - - // actual logic of resuming a job - StructuredTracer.WorkflowResuming(_workflowInstance.Id); - _workflowInstance.ResumeInstance(label); - - lock (SyncRoot) - { - // once the resume instance is called then we can allow the subsequent suspend requests - _suspending = false; - } - } - finally - { - lock (SyncRoot) - { - listOfLabels.Remove(label); - } - } - - StructuredTracer.WorkflowResumed(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "DoLabeledResumeJob", WorkflowGuidForTraces, this, "END"); - } - - private void DoResumeJobCatchException(object state) - { - var tuple = state as Tuple; - Dbg.Assert(tuple != null, "ResumeJob with exception handling should have tuple."); - try - { - DoResumeJob(tuple.Item1); - } - catch (Exception e) - { - lock (_resumeErrorSyncObject) - { - if (!_resumeErrors.ContainsKey(tuple.Item2)) - _resumeErrors.Add(tuple.Item2, e); - } - } - finally - { - // Blue: 79098 - // There was a race condition between ResumeJob(label) and DoResumeJobCatchException() in setting and accessing the resume errors. - // ManualResetEvent should be set here after adding resume errors if any. - var eventTuple = tuple.Item1 as Tuple; - if (eventTuple != null && eventTuple.Item2 != null) - { - eventTuple.Item2.Set(); - } - } - } - - /// - /// DoResumeBookmark - /// - /// The Bookmark which needs to be resumed. - /// The state, which will be passed to the activity, which gets resumed. - protected virtual void DoResumeBookmark(Bookmark bookmark, object state) - { - if (_isDisposed) return; - - lock (SyncRoot) - { - if (_isDisposed) return; - - if (this.IsFinishedState(JobStateInfo.State) || JobStateInfo.State == JobState.Stopping) - { - _tracer.WriteMessage(ClassNameTrace, "DoResumeBookmark", WorkflowGuidForTraces, this, "InvalidJobState to resume a bookmark"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.ResumeNotValidState); - } - } - - // Do Not set job state from within the lock. - // This can cause a deadlock because events are raised. - // - DoSetJobState(JobState.Running); - - _workflowInstance.ResumeBookmark(bookmark, state); - } - - private bool DoTerminateJob(string reason, bool suppressError = false) - { - if (_isDisposed) return false; - bool terminated = false; - lock (SyncRoot) - { - if (_isDisposed) return false; - _tracer.WriteMessage(ClassNameTrace, "DoTerminateJob", WorkflowGuidForTraces, this, "BEGIN"); - if (JobStateInfo.State == JobState.Running || JobStateInfo.State == JobState.NotStarted) - { - _tracer.WriteMessage("trying to terminate running workflow job"); - _workflowInstance.CheckForTerminalAction(); - _workflowInstance.TerminateInstance(reason, suppressError); - terminated = true; - } - else if (JobStateInfo.State == JobState.Suspended) - { - _tracer.WriteMessage("Trying to load and terminate suspended workflow"); - _workflowInstance.DoLoadInstanceForReactivation(); - _workflowInstance.TerminateInstance(reason, suppressError); - terminated = true; - } - } - - _tracer.WriteMessage(ClassNameTrace, "DoTerminateJob", WorkflowGuidForTraces, this, "END"); - return terminated; - } - - internal bool DoAbortJob(string reason) - { - if (_isDisposed) return false; - - bool waitForRunning = false; - - lock (SyncRoot) - { - if (_isDisposed) return false; - - if (_isDisposed || JobStateInfo.State == JobState.Suspending || JobStateInfo.State == JobState.Suspended || _suspending) - return false; - - if (_starting) - waitForRunning = true; - - _tracer.WriteMessage(ClassNameTrace, "DoAbortJob", WorkflowGuidForTraces, this, "BEGIN"); - _suspending = true; - - if (JobStateInfo.State != JobState.Running && JobStateInfo.State != JobState.NotStarted) - { - _tracer.WriteMessage(ClassNameTrace, "DoAbortJob", WorkflowGuidForTraces, this, "InvalidJobState"); - throw new InvalidJobStateException(JobStateInfo.State, Resources.SuspendNotValidState); - } - } - - // Do not set job state from within lock. - // This can cause a deadlock because events are raised. - DoSetJobState(JobState.Suspending); - - lock (SyncRoot) - { - _resuming = false; - } - - if (waitForRunning) - JobRunning.WaitOne(); - - _workflowInstance.CheckForTerminalAction(); - _workflowInstance.AbortInstance(reason); - - _tracer.WriteMessage(ClassNameTrace, "DoAbortJob", WorkflowGuidForTraces, this, "END"); - - return true; - } - - private void OnWorkflowCompleted(object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowCompleted", WorkflowGuidForTraces, this, "BEGIN"); - DoSetJobState(JobState.Completed); - StructuredTracer.WorkflowExecutionFinished(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.SucceededWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.SucceededWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowCompleted", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowAborted(Exception e, object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowAborted", WorkflowGuidForTraces, this, "BEGIN"); - - DoSetJobState(JobState.Suspended, e); - - StructuredTracer.WorkflowExecutionAborted(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.FailedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.FailedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowAborted", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowFaulted(Exception e, object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowFaulted", WorkflowGuidForTraces, this, "BEGIN"); - StructuredTracer.WorkflowExecutionError(_workflowInstance.Id, Tracer.GetExceptionString(e) + Environment.NewLine + e); - DoSetJobState(JobState.Failed, e); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.FailedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.FailedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowFaulted", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowStopped(object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowStopped", WorkflowGuidForTraces, this, "BEGIN"); - DoSetJobState(JobState.Stopped); - StructuredTracer.WorkflowExecutionCancelled(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowStopped", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowSuspended(object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowSuspended", WorkflowGuidForTraces, this, "BEGIN"); - - // CheckStopping() was not thread safe. - // Now DoSetJobState handles the invalid state transition from stopping to suspended - if (DoSetJobState(JobState.Suspended)) - { - StructuredTracer.WorkflowUnloaded(_workflowInstance.Id); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.SuspendedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.SuspendedWorkflowJobsPerSec); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount); - _perfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec); - } - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowSuspended", WorkflowGuidForTraces, this, "END"); - } - - private void OnWorkflowIdle(ReadOnlyCollection bookmarks, object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowIdle", WorkflowGuidForTraces, this, "BEGIN"); - - if (this.OnIdle != null) OnIdle(this, bookmarks); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowIdle", WorkflowGuidForTraces, this, "END"); - } - - private PSPersistableIdleAction OnWorkflowPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest, object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowPersistIdleAction", WorkflowGuidForTraces, this, "BEGIN"); - - PSPersistableIdleAction rc = PSPersistableIdleAction.NotDefined; - - if (this.OnPersistableIdleAction != null) - rc = this.OnPersistableIdleAction(this, bookmarks, externalSuspendRequest); - - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowPersistIdleAction", WorkflowGuidForTraces, this, "END"); - - return rc; - } - - private void OnWorkflowUnloaded(object sender) - { - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowUnloaded", WorkflowGuidForTraces, this, "BEGIN"); - if (this.OnUnloaded != null) OnUnloaded(this); - _tracer.WriteMessage(ClassNameTrace, "OnWorkflowUnloaded", WorkflowGuidForTraces, this, "END"); - } - - /// - /// Unloads the streams of the job. - /// - /// - /// To be called from this class only - private void SelfUnloadJobStreams() - { - if (_hasMoreDataOnDisk) return; - lock (SyncRoot) - { - if (_hasMoreDataOnDisk) return; - - Dbg.Assert(JobStateInfo.State == JobState.Stopped || - JobStateInfo.State == JobState.Completed || - JobStateInfo.State == JobState.Failed || - JobStateInfo.State == JobState.Suspended, "Job state is incorrect when unload is called"); - UnloadJobStreams(); - } - } - - #endregion Private Methods - - #region Internal Accessors - - internal Guid WorkflowGuid { get { return _workflowInstance.Id; } } - private Guid WorkflowGuidForTraces { get { if (_workflowInstance != null) return _workflowInstance.Id; return Guid.Empty; } } - - internal bool WorkflowInstanceLoaded { get; set; } - - internal bool SynchronousExecution { get; set; } - - internal bool? IsSuspendable = null; - - internal PSLanguageMode? SourceLanguageMode { get; set; } - - #endregion Internal Accessors - - #region Internal Methods - - private bool _unloadStreamsOnPersistentState; - internal void EnableStreamUnloadOnPersistentState() - { - _unloadStreamsOnPersistentState = true; - } - - /// - /// Helper function to check if job is finished - /// - /// - /// - internal bool IsFinishedState(JobState state) - { - return (state == JobState.Completed || state == JobState.Failed || state == JobState.Stopped); - } - - internal void LoadWorkflow(CommandParameterCollection commandParameterCollection, Activity activity, string xaml) - { - _tracer.WriteMessage(ClassNameTrace, "LoadWorkflow", WorkflowGuidForTraces, this, "BEGIN"); - Dbg.Assert(_workflowInstance == null, "LoadWorkflow() should only be called once by the adapter"); - - // If activity hasn't been cached, we can't generate it from _definition. - if (activity == null) - { - bool windowsWorkflow; - activity = DefinitionCache.Instance.GetActivityFromCache(_definition, out windowsWorkflow); - - if (activity == null) - { - // The workflow cannot be run. - throw new InvalidOperationException(Resources.ActivityNotCached); - } - } - - string workflowXaml; - string runtimeAssemblyPath; - - if (string.IsNullOrEmpty(xaml)) - { - workflowXaml = DefinitionCache.Instance.GetWorkflowXaml(_definition); - runtimeAssemblyPath = DefinitionCache.Instance.GetRuntimeAssemblyPath(_definition); - } - else - { - workflowXaml = xaml; - runtimeAssemblyPath = null; - } - - _location = null; - SortStartParameters(activity as DynamicActivity, commandParameterCollection); - - // Set location if ComputerName wasn't specified in the parameters. - if (string.IsNullOrEmpty(_location)) _location = Constants.DefaultComputerName; - if (_jobMetadata.ContainsKey(Constants.JobMetadataLocation)) - _jobMetadata.Remove(Constants.JobMetadataLocation); - _jobMetadata.Add(Constants.JobMetadataLocation, _location); - - if (_jobMetadata.ContainsKey(Constants.WorkflowJobCreationContext)) - _jobMetadata.Remove(Constants.WorkflowJobCreationContext); - _jobMetadata.Add(Constants.WorkflowJobCreationContext, _jobCreationContext); - - PSWorkflowDefinition definition = new PSWorkflowDefinition(activity, workflowXaml, runtimeAssemblyPath, _definition.RequiredAssemblies); - PSWorkflowContext metadatas = new PSWorkflowContext(_workflowParameters, _psWorkflowCommonParameters, _jobMetadata, _privateMetadata); - - _workflowInstance = _runtime.Configuration.CreatePSWorkflowInstance(definition, metadatas, _inputCollection, this); - this.ConfigureWorkflowHandlers(); - - // Create a WorkflowApplication instance. - _tracer.WriteMessage(ClassNameTrace, "LoadWorkflow", WorkflowGuidForTraces, this, "Calling instance loader"); - - #if DEBUG - try - { - _workflowInstance.CreateInstance(); - } - catch (Exception e) - { - if (e.Message.IndexOf("Cannot create unknown type", StringComparison.OrdinalIgnoreCase) >= 0) - { - // Capture environment to help diagnose: MSFT:246456 - PSObject inputObject = new PSObject(); - inputObject.Properties.Add( - new PSNoteProperty("activity", activity)); - inputObject.Properties.Add( - new PSNoteProperty("workflowXaml", workflowXaml)); - inputObject.Properties.Add( - new PSNoteProperty("runtimeAssemblyPath", runtimeAssemblyPath)); - inputObject.Properties.Add( - new PSNoteProperty("_definition.RequiredAssemblies", _definition.RequiredAssemblies)); - - string tempPath = System.IO.Path.GetTempFileName(); - System.Management.Automation.PowerShell.Create().AddCommand("Export-CliXml"). - AddParameter("InputObject", inputObject). - AddParameter("Depth", 10). - AddParameter("Path", tempPath).Invoke(); - - throw new Exception("Bug MSFT:246456 detected. Please capture " + tempPath + ", open a new issue " + - "at https://github.com/PowerShell/PowerShell/issues/new and attach the file."); - } - else - { - throw; - } - } - #else - _workflowInstance.CreateInstance(); - #endif - - InitializeWithWorkflow(_workflowInstance); - WorkflowInstanceLoaded = true; - _tracer.WriteMessage(ClassNameTrace, "LoadWorkflow", WorkflowGuidForTraces, this, "END"); - } - - internal void ConfigureWorkflowHandlers() - { - _workflowInstance.OnCompletedDelegate = OnWorkflowCompleted; - _workflowInstance.OnSuspendedDelegate = OnWorkflowSuspended; - _workflowInstance.OnStoppedDelegate = OnWorkflowStopped; - _workflowInstance.OnAbortedDelegate = OnWorkflowAborted; - _workflowInstance.OnFaultedDelegate = OnWorkflowFaulted; - _workflowInstance.OnIdleDelegate = OnWorkflowIdle; - _workflowInstance.OnPersistableIdleActionDelegate = OnWorkflowPersistableIdleAction; - _workflowInstance.OnUnloadedDelegate = OnWorkflowUnloaded; - } - - internal void RestoreFromWorkflowInstance(PSWorkflowInstance instance) - { - _tracer.WriteMessage(ClassNameTrace, "RestoreFromWorkflowInstance", WorkflowGuidForTraces, this, "BEGIN"); - Dbg.Assert(instance != null, "cannot restore a workflow job with null workflow instance"); - - object data; - Exception reason = null; - - if (instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataStateReason, out data)) - reason = data as Exception; - else - reason = instance.Error; - - // igorse: restore all of the job metadata - _workflowParameters = instance.PSWorkflowContext.WorkflowParameters; - _psWorkflowCommonParameters = instance.PSWorkflowContext.PSWorkflowCommonParameters; - _jobMetadata = instance.PSWorkflowContext.JobMetadata; - _privateMetadata = instance.PSWorkflowContext.PrivateMetadata; - - if (instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataLocation, out data)) _location = data as string; - if (instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataStatusMessage, out data)) _statusMessage = data as string; - if (instance.State == JobState.Suspended) - { - // Status message cannot be set publicly. - _statusMessage = Resources.SuspendedJobRecoveredFromPreviousSession; - } - - // indicate that this job has results on disk. This will ensure - // that the results are not loaded when doing a get-job - lock (_syncObject) - { - _hasMoreDataOnDisk = true; - } - - Dbg.Assert(instance.JobStateRetrieved, - "Cannot set job state when job state is not retrieved or when there is an error in retrieval"); - // igorse: set job state when job is fully restored as StateChanged event will fire - DoSetJobState(instance.State, reason); - _tracer.WriteMessage(ClassNameTrace, "RestoreFromWorkflowInstance", WorkflowGuidForTraces, this, "END"); - } - - #endregion Internal Methods - - #region Async helpers - - private enum ActionType - { - Start = 0, - Stop = 1, - Suspend = 2, - Resume = 3, - Abort = 4, - Terminate = 5 - } - - private class AsyncCompleteContainer - { - internal AsyncCompletedEventArgs EventArgs; - internal ActionType Action; - } - - private delegate void JobActionWorkerDelegate(AsyncOperation asyncOp, ActionType action, string reason, string label, bool suppressError); - private void JobActionWorker(AsyncOperation asyncOp, ActionType action, string reason, string label, bool suppressError) - { - Exception exception = null; - -#pragma warning disable 56500 - try - { - switch (action) - { - case ActionType.Start: - DoStartJobLogic(null); - break; - - case ActionType.Stop: - DoStopJob(); - break; - - case ActionType.Suspend: - DoSuspendJob(); - break; - - case ActionType.Resume: - DoResumeJob(label); - break; - - case ActionType.Abort: - DoAbortJob(reason); - break; - - case ActionType.Terminate: - DoTerminateJob(reason, suppressError); - break; - } - } - catch (Exception e) - { - // Called on a background thread, need to include any exception in - // event arguments. - exception = e; - } -#pragma warning restore 56500 - - var eventArgs = new AsyncCompletedEventArgs(exception, false, asyncOp.UserSuppliedState); - - var container = new AsyncCompleteContainer { EventArgs = eventArgs, Action = action }; - - // End the task. The asyncOp object is responsible - // for marshaling the call. - asyncOp.PostOperationCompleted(JobActionAsyncCompleted, container); - } - - private void JobActionAsyncCompleted(object operationState) - { - if (_isDisposed) return; - var container = operationState as AsyncCompleteContainer; - Dbg.Assert(container != null, "AsyncCompleteContainer cannot be null"); - _tracer.WriteMessage(ClassNameTrace, "JobActionAsyncCompleted", WorkflowGuidForTraces, this, "operation: {0}", container.Action.ToString()); - try - { - switch (container.Action) - { - case ActionType.Start: - if (container.EventArgs.Error == null) JobRunning.WaitOne(); - OnStartJobCompleted(container.EventArgs); - break; - case ActionType.Stop: - case ActionType.Terminate: - if (container.EventArgs.Error == null) Finished.WaitOne(); - OnStopJobCompleted(container.EventArgs); - break; - case ActionType.Suspend: - if (container.EventArgs.Error == null) JobSuspendedOrAborted.WaitOne(); - OnSuspendJobCompleted(container.EventArgs); - break; - case ActionType.Abort: - if (container.EventArgs.Error == null) JobSuspendedOrAborted.WaitOne(); - OnSuspendJobCompleted(container.EventArgs); - break; - case ActionType.Resume: - if (container.EventArgs.Error == null) JobRunning.WaitOne(); - OnResumeJobCompleted(container.EventArgs); - break; - } - } - catch (ObjectDisposedException) - { - // An object disposed exception can be thrown by any of the above WaitOne() statements. - // To otherwise prevent this, locking could be done. That would perf implications for every - // iteration of this code, and would open a possibility for deadlocks through multiple lock - // objects. - } - } - - #endregion Async helpers - - #region Constructors - - /// - /// Construct a PSWorkflowJob. - /// - /// - /// JobInvocationInfo representing the command this job - /// will invoke. - internal PSWorkflowJob(PSWorkflowRuntime runtime, JobInvocationInfo specification) - : base(Validate(specification).Command) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - // If specification is null, ArgumentNullException would be raised from - // the static validate method. - StartParameters = specification.Parameters; - _definition = WorkflowJobDefinition.AsWorkflowJobDefinition(specification.Definition); - - _runtime = runtime; - CommonInit(); - } - - /// - /// Construct a PSWorkflowJob. - /// - /// - /// JobInvocationInfo representing the command this job will invoke. - /// - /// - internal PSWorkflowJob(PSWorkflowRuntime runtime, JobInvocationInfo specification, Guid JobInstanceId, Dictionary creationContext) - : base(Validate(specification).Command, specification.Definition.Name, JobInstanceId) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - // If specification is null, ArgumentNullException would be raised from - // the static validate method. - StartParameters = specification.Parameters; - _definition = WorkflowJobDefinition.AsWorkflowJobDefinition(specification.Definition); - _jobCreationContext = creationContext; - - _runtime = runtime; - CommonInit(); - } - - /// - /// - /// - /// - /// - /// - internal PSWorkflowJob(PSWorkflowRuntime runtime, string command, string name) - : base(command, name) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - - _runtime = runtime; - CommonInit(); - } - - /// - /// - /// - /// - /// - /// - /// - internal PSWorkflowJob(PSWorkflowRuntime runtime, string command, string name, JobIdentifier token) - : base(command, name, token) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - - _runtime = runtime; - CommonInit(); - } - - /// - /// - /// - /// - /// - /// - /// - internal PSWorkflowJob(PSWorkflowRuntime runtime, string command, string name, Guid instanceId) - : base(command, name, instanceId) - { - Dbg.Assert(runtime != null, "runtime must not be null."); - - _runtime = runtime; - CommonInit(); - } - - private void CommonInit() - { - PSJobTypeName = WorkflowJobSourceAdapter.AdapterTypeName; - StateChanged += HandleMyStateChanged; - _tracer.WriteMessage(ClassNameTrace, "CommonInit", WorkflowGuidForTraces, this, "Construction/initialization"); - - } - - #endregion Constructors - - #region Overrides of Job - - /// - /// - /// - public override void StopJob() - { - AssertNotDisposed(); - DoStopJob(); - Finished.WaitOne(); - } - - /// - /// Success status of the command execution. - /// - public override string StatusMessage - { - get { return _statusMessage; } - } - - /// - /// Indicates that more data is available in this result object for reading. - /// - public override bool HasMoreData - { - get - { - return (_hasMoreDataOnDisk - || Output.IsOpen || Output.Count > 0 - || Error.IsOpen || Error.Count > 0 - || Verbose.IsOpen || Verbose.Count > 0 - || Debug.IsOpen || Debug.Count > 0 - || Warning.IsOpen || Warning.Count > 0 - || Progress.IsOpen || Progress.Count > 0 - || Information.IsOpen || Information.Count > 0 - ); - } - } - - private bool _hasMoreDataOnDisk; - - /// - /// Indicates a location where this job is running. - /// - public override string Location - { - get { return _location; } - } - - #endregion - - #region Overrides of Job2 - - /// - /// Implementation of this method will allow the delayed loading of streams. - /// - protected override void DoLoadJobStreams() - { - lock (_syncObject) - { - bool closeStreams = IsFinishedState(JobStateInfo.State); - InitializeWithWorkflow(PSWorkflowInstance, closeStreams); - _hasMoreDataOnDisk = false; - } - } - - /// - /// Unloads job streams information. Enables jobs to - /// clear stream information from memory - /// - protected override void DoUnloadJobStreams() - { - if (_workflowInstance == null) return; - - lock (_syncObject) - { - if (_workflowInstance == null) return; - - // if persisting the streams was necessary and - // there was an error it means that the streams - // contain objects that are not serializable - // In such a case we should not dispose the - // streams from memory - if (!_workflowInstance.SaveStreamsIfNecessary()) return; - - _hasMoreDataOnDisk = true; - _workflowInstance.DisposeStreams(); - } - } - - /// - /// start a job. The job will be started with the parameters - /// specified in StartParameters - /// - /// It is redundant to have a method named StartJob - /// on a job class. However, this is done so as to avoid - /// an FxCop violation "CA1716:IdentifiersShouldNotMatchKeywords" - /// Stop and Resume are reserved keyworks in C# and hence cannot - /// be used as method names. Therefore to be consistent it has - /// been decided to use *Job in the name of the methods - public override void StartJob() - { - AssertValidState(JobState.NotStarted); - _isAsync = false; - _runtime.JobManager.SubmitOperation(this, DoStartJobLogic, null, JobState.NotStarted); - JobRunning.WaitOne(); - } - - /// - /// Start a job asynchronously - /// - public override void StartJobAsync() - { -#pragma warning disable 56500 - try - { - // Assert here and catch it for event arguments to reduce the number - // of error operations in the throttle manager. - AssertValidState(JobState.NotStarted); - _runtime.JobManager.SubmitOperation(this, DoStartJobAsync, null, JobState.NotStarted); - } - catch (Exception e) - { - // Catching all exceptions is valid here because - // Transferring exception with event arguments. - OnStartJobCompleted(new AsyncCompletedEventArgs(e, false, null)); - } -#pragma warning restore 56500 - } - - /// - /// Stop a job asynchronously - /// - public override void StopJobAsync() - { - AssertNotDisposed(); - _tracer.WriteMessage(ClassNameTrace, "StopJobAsync", WorkflowGuidForTraces, this, ""); - var asyncOp = AsyncOperationManager.CreateOperation(null); - - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - workerDelegate.BeginInvoke( - asyncOp, - ActionType.Stop, - string.Empty, - string.Empty, - false, - null, - null); - } - - /// - /// Suspend a job - /// - public override void SuspendJob() - { - AssertNotDisposed(); - - DoSuspendJob(); - - // DoSuspendJob returns a bool, but - // in the case of a SuspendJob call, we do not - // want to return until the job is in the correct state. - // The boolean return value is used by the WorkflowManager - // for Shutdown purposes. - JobSuspendedOrAborted.WaitOne(); - } - - /// - /// Asynchronously suspend a job - /// - public override void SuspendJobAsync() - { - AssertNotDisposed(); - _tracer.WriteMessage(ClassNameTrace, "SuspendJobAsync", WorkflowGuidForTraces, this, ""); - var asyncOp = AsyncOperationManager.CreateOperation(null); - - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - workerDelegate.BeginInvoke( - asyncOp, - ActionType.Suspend, - string.Empty, - string.Empty, - false, - null, - null); - } - - /// - /// Stop Job - /// - /// True to force stop - /// Reason for forced stop - public override void StopJob(bool force, string reason) - { - StopJob(force, reason, false); - } - - /// - /// Stop Job - /// - /// True to force stop - /// Reason for forced stop - /// Suppress error for forced stop - public void StopJob(bool force, string reason, bool suppressError) - { - AssertNotDisposed(); - - if (force) - { - if (DoTerminateJob(reason, suppressError)) - Finished.WaitOne(); - } - else - { - DoStopJob(); - Finished.WaitOne(); - } - } - - /// - /// Stop Job Asynchronously - /// - /// True to force stop - /// Reason for forced stop - public override void StopJobAsync(bool force, string reason) - { - StopJobAsync(force, reason, false); - } - - /// - /// Stop job asynchronously - /// - /// True to force stop - /// Reason for forced stop - /// Suppress error for forced stop - public void StopJobAsync(bool force, string reason, bool suppressError) - { - AssertNotDisposed(); - _tracer.WriteMessage(ClassNameTrace, "StopJobAsync", WorkflowGuidForTraces, this, ""); - - var asyncOp = AsyncOperationManager.CreateOperation(null); - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - - ActionType actionType = ActionType.Stop; - - if (force) - { - actionType = ActionType.Terminate; - } - - workerDelegate.BeginInvoke( - asyncOp, - actionType, - reason, - string.Empty, - suppressError, - null, - null); - } - - /// - /// Suspend Job - /// - /// - /// - public override void SuspendJob(bool force, string reason) - { - AssertNotDisposed(); - - if (force) - { - DoAbortJob(reason); - JobSuspendedOrAborted.WaitOne(); - } - else - { - DoSuspendJob(); - JobSuspendedOrAborted.WaitOne(); - } - } - - /// - /// Suspend Job Asynchronously - /// - /// - /// - public override void SuspendJobAsync(bool force, string reason) - { - AssertNotDisposed(); - _tracer.WriteMessage(ClassNameTrace, "SuspendJobAsync", WorkflowGuidForTraces, this, ""); - - var asyncOp = AsyncOperationManager.CreateOperation(null); - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - ActionType actionType = ActionType.Suspend; - - if (force) - { - actionType = ActionType.Abort; - } - - workerDelegate.BeginInvoke( - asyncOp, - actionType, - reason, - string.Empty, - false, - null, - null); - } - - /// - /// Resume a suspended job - /// - public override void ResumeJob() - { - ResumeJob(null); - } - - /// - /// Resume a suspended job - /// - /// - public virtual void ResumeJob(string label) - { - AssertNotDisposed(); - var errorId = Guid.NewGuid(); - var waitHandle = new ManualResetEvent(false); - _statusMessage = Resources.JobQueuedForResume; - _runtime.JobManager.SubmitOperation(this, DoResumeJobCatchException, new Tuple(new Tuple(label, waitHandle), errorId), JobState.Suspended); - waitHandle.WaitOne(); - waitHandle.Dispose(); - - lock (_resumeErrorSyncObject) - { - Exception exception; - if (_resumeErrors.TryGetValue(errorId, out exception)) - { - _resumeErrors.Remove(errorId); - throw exception; - } - } - } - - /// - /// ResumeBookmark - /// - /// - /// - public void ResumeBookmark(Bookmark bookmark, object state) - { - if (bookmark == null) throw new ArgumentNullException("bookmark"); - - DoResumeBookmark(bookmark, state); - } - - /// - /// ResumeBookmark - /// - /// - /// - /// - public void ResumeBookmark(Bookmark bookmark, bool supportDisconnectedStreams, PowerShellStreams streams) - { - if (bookmark == null) throw new ArgumentNullException("bookmark"); - if (streams == null) throw new ArgumentNullException("streams"); - - PSResumableActivityContext arguments = new PSResumableActivityContext(streams); - arguments.SupportDisconnectedStreams = supportDisconnectedStreams; - arguments.Failed = false; - arguments.Error = null; - - DoResumeBookmark(bookmark, arguments); - } - - /// - /// ResumeBookmark - /// - /// - /// - /// - /// - public void ResumeBookmark(Bookmark bookmark, bool supportDisconnectedStreams, PowerShellStreams streams, Exception exception) - { - if (bookmark == null) throw new ArgumentNullException("bookmark"); - if (streams == null) throw new ArgumentNullException("streams"); - if (exception == null) throw new ArgumentNullException("exception"); - - PSResumableActivityContext arguments = new PSResumableActivityContext(streams); - arguments.SupportDisconnectedStreams = supportDisconnectedStreams; - arguments.Failed = true; - arguments.Error = exception; - - DoResumeBookmark(bookmark, arguments); - } - - /// - /// GetPersistableIdleAction - /// - /// - /// - /// - public PSPersistableIdleAction GetPersistableIdleAction(ReadOnlyCollection bookmarks, bool externalSuspendRequest) - { - PSPersistableIdleAction defaultValue = this.PSWorkflowInstance.GetPersistableIdleAction(bookmarks, externalSuspendRequest); - - if (defaultValue == PSPersistableIdleAction.Suspend && listOfLabels.Count > 0) - { - // Labeled resumption is in progress so we cannot allow the suspension - return PSPersistableIdleAction.None; - } - - - lock (_syncObject) - { - if (defaultValue == PSPersistableIdleAction.Suspend && listOfLabels.Count > 0) - { - // Labeled resumption is in progress so we cannot allow the suspension - return PSPersistableIdleAction.None; - } - - if (defaultValue == PSPersistableIdleAction.Suspend) - wfSuspendInProgress = true; - - return defaultValue; - } - } - - private bool wfSuspendInProgress = false; - private List listOfLabels = new List(); - - - /// - /// Resume a suspended job asynchronously. - /// - public override void ResumeJobAsync() - { -#pragma warning disable 56500 - try - { - AssertNotDisposed(); - _runtime.JobManager.SubmitOperation(this, DoResumeJobAsync, null, JobState.Suspended); - } - catch (Exception e) - { - // Catching all exceptions is valid here because - // Transferring exception with event arguments. - OnResumeJobCompleted(new AsyncCompletedEventArgs(e, false, null)); - } -#pragma warning restore 56500 - } - - private void DoResumeJobAsync(object state) - { - _tracer.WriteMessage(ClassNameTrace, "DoResumeJobAsync", WorkflowGuidForTraces, this, ""); - var label = state as string; - var asyncOp = AsyncOperationManager.CreateOperation(null); - - var workerDelegate = new JobActionWorkerDelegate(JobActionWorker); - workerDelegate.BeginInvoke( - asyncOp, - ActionType.Resume, - string.Empty, - label, - false, - null, - null); - } - - /// - /// Resume a suspended job asynchronously. - /// - /// - public virtual void ResumeJobAsync(string label) - { -#pragma warning disable 56500 - try - { - AssertNotDisposed(); - _runtime.JobManager.SubmitOperation(this, DoResumeJobAsync, label, JobState.Suspended); - } - catch (Exception e) - { - // Catching all exceptions is valid here because - // Transferring exception with event arguments. - OnResumeJobCompleted(new AsyncCompletedEventArgs(e, false, null)); - } -#pragma warning restore 56500 - } - - /// - /// Unblock a blocked job - /// - public override void UnblockJob() - { - throw new NotSupportedException(); - } - - /// - /// Unblock a blocked job asynchronously - /// - public override void UnblockJobAsync() - { - throw new NotSupportedException(); - } - - #endregion - - #region IDisposable - - /// - /// - /// - /// - protected override void Dispose(bool disposing) - { - if (_isDisposed) return; - - lock (SyncRoot) - { - if (_isDisposed) return; - _isDisposed = true; - } - - if (disposing) - { - try - { - // WorkflowInstance should be the first one to be disposed - // - if (_workflowInstance != null) - _workflowInstance.Dispose(); - - StateChanged -= HandleMyStateChanged; - - if (_jobRunning != null) - _jobRunning.Close(); - - if (_jobSuspendedOrAborted != null) - _jobSuspendedOrAborted.Dispose(); - - _tracer.Dispose(); - } - finally - { - base.Dispose(true); - } - } - } - - private void AssertNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("PSWorkflowJob"); - } - } - - /// - /// Check and add StateChanged event handler - /// - /// - /// - /// - internal bool CheckAndAddStateChangedEventHandler(EventHandler handler, JobState expectedState) - { - lock(SyncRoot) - { - // Event Handler should be added - // if JobState is not changed after job submitted to the Workflow job manager queue, OR - // if job is already running due to other labelled resume operation or other start job operation. - // - if (JobStateInfo.State == expectedState || JobStateInfo.State == JobState.Running) - { - Dbg.Assert(expectedState == JobState.NotStarted || expectedState == JobState.Suspended, "Only Start and Resume operations are serviced by PSWorkflowJobManager.StartOperationsFromQueue"); - StateChanged += handler; - return true; - } - else - { - return false; - } - } - } - - #endregion IDisposable - - #region IJobDebugger - - /// - /// Job Debugger - /// - public Debugger Debugger - { - get - { - return PSWorkflowDebugger; - } - } - - /// - /// True if job is synchronous and can be debugged. - /// - public bool IsAsync - { - get { return _isAsync; } - set { _isAsync = value; } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJobSourceAdapter.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJobSourceAdapter.cs deleted file mode 100644 index 2c9f6c71b6e..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowJobSourceAdapter.cs +++ /dev/null @@ -1,1019 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Activities; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Tracing; -using System.Management.Automation.Remoting.WSMan; -using Dbg = System.Diagnostics.Debug; -using System.Management.Automation.Remoting; - - -// Stops compiler from warning about unknown warnings -#pragma warning disable 1634, 1691 - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// Adapter that allows workflow instances to be exposed as jobs in PowerShell. - /// NOTE: This class has been unsealed to allow extensibility for Opalis. Further - /// thought is needed around the best way to enable reuse of this class. - /// - public sealed class WorkflowJobSourceAdapter : JobSourceAdapter - { - #region ContainerParentJobRepository - - private class ContainerParentJobRepository : Repository - { - /// - /// - /// - /// - /// - protected override Guid GetKey(ContainerParentJob item) - { - return item.InstanceId; - } - - internal ContainerParentJobRepository(string identifier): base(identifier) - { - - } - } - #endregion ContainerParentJobRepository - - #region Members - - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private readonly Tracer _structuredTracer = new Tracer(); - - private static readonly WorkflowJobSourceAdapter Instance = new WorkflowJobSourceAdapter(); - - private readonly ContainerParentJobRepository _jobRepository = - new ContainerParentJobRepository("WorkflowJobSourceAdapterRepository"); - - private readonly object _syncObject = new object(); - private bool _repositoryPopulated; - internal const string AdapterTypeName = "PSWorkflowJob"; - - #endregion Members - - #region Overrides of JobSourceAdapter - - /// - /// Gets the WorkflowJobSourceAdapter instance. - /// - public static WorkflowJobSourceAdapter GetInstance() - { - return Instance; - } - - private PSWorkflowJobManager _jobManager; - /// - /// GetJobManager - /// - /// - public PSWorkflowJobManager GetJobManager() - { - if (_jobManager != null) - return _jobManager; - - lock (_syncObject) - { - if (_jobManager != null) - return _jobManager; - - _jobManager = PSWorkflowRuntime.Instance.JobManager; - } - return _jobManager; - } - - /// - /// GetPSWorkflowRunTime - /// - /// - public PSWorkflowRuntime GetPSWorkflowRuntime() - { - if (_runtime != null) - return _runtime; - - lock (_syncObject) - { - if (_runtime != null) - return _runtime; - - _runtime = PSWorkflowRuntime.Instance; - } - return _runtime; - } - - private PSWorkflowRuntime _runtime; - private PSWorkflowValidator _wfValidator; - internal PSWorkflowValidator GetWorkflowValidator() - { - if (_wfValidator != null) - return _wfValidator; - - lock (_syncObject) - { - if (_wfValidator != null) - return _wfValidator; - - _wfValidator = new PSWorkflowValidator(PSWorkflowRuntime.Instance.Configuration); - } - return _wfValidator; - } - - /// - /// Create a new job with the specified JobSpecification. - /// - /// specification - /// job object - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] - public override Job2 NewJob(JobInvocationInfo specification) - { - if (specification == null) - throw new ArgumentNullException("specification"); - - if (specification.Definition == null) - throw new ArgumentException(Resources.NewJobDefinitionNull, "specification"); - - if (specification.Definition.JobSourceAdapterType != GetType()) - throw new InvalidOperationException(Resources.NewJobWrongType); - - if (specification.Parameters.Count == 0) - { - // If there are no parameters passed in, we create one child job with nothing specified. - specification.Parameters.Add(new CommandParameterCollection()); - } - - // validation has to happen before job creation - bool? isSuspendable = null; - Activity activity = ValidateWorkflow(specification, null, ref isSuspendable); - ContainerParentJob newJob = GetJobManager().CreateJob(specification, activity); - - // We do not want to generate the warning message if the request is coming from - // Server Manager - if (PSSessionConfigurationData.IsServerManager == false) - { - foreach (PSWorkflowJob job in newJob.ChildJobs) - { - bool? psPersistValue = null; - PSWorkflowContext context = job.PSWorkflowInstance.PSWorkflowContext; - if (context != null && context.PSWorkflowCommonParameters != null && context.PSWorkflowCommonParameters.ContainsKey(Constants.Persist)) - { - psPersistValue = context.PSWorkflowCommonParameters[Constants.Persist] as bool?; - } - - // check for invocation time pspersist value if not true then there is a possibility that workflow is not suspendable. - if (psPersistValue == null || (psPersistValue == false)) - { - // check for authoring time definition of persist activity - if (isSuspendable != null && isSuspendable.Value == false) - { - job.Warning.Add(new WarningRecord(Resources.WarningMessageForPersistence)); - job.IsSuspendable = isSuspendable; - } - } - - } - } - - StoreJobIdForReuse(newJob, true); - _jobRepository.Add(newJob); - - return newJob; - } - - /// - /// Get the list of jobs that are currently available in the workflow instance table. - /// - /// collection of job objects - public override IList GetJobs() - { - _tracer.WriteMessage("WorkflowJobSourceAdapter: Getting all Workflow jobs"); - PopulateJobRepositoryIfRequired(); - return new List(_jobRepository.GetItems()); - } - - /// - /// Get list of jobs that matches the specified names - /// - /// names to match, can support - /// wildcard if the store supports - /// - /// collection of jobs that match the specified - /// criteria - public override IList GetJobsByName(string name, bool recurse) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow jobs by name: {0}", name)); - PopulateJobRepositoryIfRequired(); - WildcardPattern patternForName = new WildcardPattern(name, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - - List selectedJobs = new List(); - var jobs = _jobRepository.GetItems().Where(parentJob => patternForName.IsMatch(parentJob.Name)).Cast(). - ToList(); - selectedJobs.AddRange(jobs); - - if (recurse) - { - var filter = new Dictionary { { Constants.JobMetadataName, patternForName } }; - var childJobs = GetJobManager().GetJobs(GetChildJobsFromRepository(), WorkflowFilterTypes.JobMetadata, filter); - selectedJobs.AddRange(childJobs); - } - - return selectedJobs; - } - - /// - /// Get list of jobs that run the specified command - /// - /// command to match - /// - /// collection of jobs that match the specified - /// criteria - public override IList GetJobsByCommand(string command, bool recurse) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow jobs by command: {0}", command)); - PopulateJobRepositoryIfRequired(); - List jobs = new List(); - WildcardPattern patternForCommand = new WildcardPattern(command, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - - var list = - _jobRepository.GetItems().Where(parentJob => patternForCommand.IsMatch(parentJob.Command)).Cast(). - ToList(); - if (list.Count > 0) - jobs.AddRange(list); - - if (recurse) - { - var filter = new Dictionary {{Constants.JobMetadataParentCommand, patternForCommand}}; - var listChildJobs = GetJobManager().GetJobs(GetChildJobsFromRepository(), WorkflowFilterTypes.JobMetadata, - filter); - jobs.AddRange(listChildJobs); - } - - return jobs; - } - - /// - /// Get list of jobs that has the specified id - /// - /// Guid to match - /// - /// job with the specified guid - public override Job2 GetJobByInstanceId(Guid instanceId, bool recurse) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow job by instance id: {0}", instanceId)); - - // there can be a lot of jobs in the repository. First search whats in - // memory before rehydrating from disk - Job2 selectedJob = _jobRepository.GetItem(instanceId); - if (selectedJob == null) - { - PopulateJobRepositoryIfRequired(); - selectedJob = _jobRepository.GetItem(instanceId); - } - - if (selectedJob != null) return selectedJob; - - if (recurse) - { - selectedJob = GetChildJobsFromRepository().FirstOrDefault(job => job.InstanceId == instanceId); - } - return selectedJob; - } - - /// - /// Get job by session id. - /// - /// The session id. - /// - /// The returned job2 object. - public override Job2 GetJobBySessionId(int id, bool recurse) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow job by session id: {0}", id)); - - PopulateJobRepositoryIfRequired(); - - Job2 selectedJob = _jobRepository.GetItems().FirstOrDefault(job => job.Id == id); - if (selectedJob != null) return selectedJob; - - if (recurse) - { - selectedJob = GetChildJobsFromRepository().FirstOrDefault(job => job.Id == id); - } - return selectedJob; - } - - /// - /// Get list of jobs that are in the specified state - /// - /// state to match - /// - /// collection of jobs with the specified - /// state - public override IList GetJobsByState(JobState state, bool recurse) - { - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Getting Workflow jobs by state: {0}", state)); - - PopulateJobRepositoryIfRequired(); - // Parent states are derived from child states, so a simple filter will not work. - List jobs = recurse - ? GetChildJobsFromRepository().Where(job => job.JobStateInfo.State == state).Cast - ().ToList() - : _jobRepository.GetItems().Where(job => job.JobStateInfo.State == state).Cast() - .ToList(); - return jobs; - } - - /// - /// Get list of jobs based on the adapter specific - /// filter parameters - /// - /// dictionary containing name value - /// pairs for adapter specific filters - /// - /// collection of jobs that match the - /// specified criteria - public override IList GetJobsByFilter(Dictionary filter, bool recurse) - { - if (filter == null) - { - throw new ArgumentNullException("filter"); - } - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Getting Workflow jobs by filter: {0}", filter)); - PopulateJobRepositoryIfRequired(); - - // Do not modify the user's collection. - Dictionary filter2 = new Dictionary(filter, StringComparer.CurrentCultureIgnoreCase); - bool addPid = false; - bool searchParentJobs = true; - if (filter2.Keys.Count == 0) - searchParentJobs = false; - else - { - if (filter2.Keys.Any(key => (((!key.Equals(Constants.JobMetadataSessionId, StringComparison.OrdinalIgnoreCase) && !key.Equals(Constants.JobMetadataInstanceId, StringComparison.OrdinalIgnoreCase)) && !key.Equals(Constants.JobMetadataName, StringComparison.OrdinalIgnoreCase)) && !key.Equals(Constants.JobMetadataCommand, StringComparison.OrdinalIgnoreCase)) && !key.Equals(Constants.JobMetadataFilterState, StringComparison.OrdinalIgnoreCase))) - { - searchParentJobs = false; - } - } - - List jobs = new List(); - // search container parent jobs first - if (searchParentJobs) - { - List repositoryJobs = _jobRepository.GetItems(); - - List searchList = SearchJobsOnV2Parameters(repositoryJobs, filter2); - repositoryJobs.Clear(); - - if (searchList.Count > 0) - { - jobs.AddRange(searchList); - } - } - - if (recurse) - { - // If the session Id parameter is present, make sure that the Id match is valid by adding the process Id to the filter. - if (filter2.ContainsKey(Constants.JobMetadataSessionId)) - addPid = true; - - if (addPid) filter2.Add(Constants.JobMetadataPid, Process.GetCurrentProcess().Id); - - if (filter2.ContainsKey(Constants.JobMetadataFilterState)) - { - filter2.Remove(Constants.JobMetadataFilterState); - } - - LoadWorkflowInstancesFromStore(); - - // remove state from filter here and do it separately - IEnumerable workflowInstances = GetJobManager().GetJobs(WorkflowFilterTypes.All, filter2); - - if (filter.ContainsKey(Constants.JobMetadataFilterState)) - { - JobState searchState = - (JobState) - LanguagePrimitives.ConvertTo(filter[Constants.JobMetadataFilterState], typeof(JobState), CultureInfo.InvariantCulture); - var list = workflowInstances.Where(job => job.JobStateInfo.State == searchState).ToList(); - jobs.AddRange(list); - } - else - { - jobs.AddRange(workflowInstances); - } - } - - List cpjs = new List(); - foreach (var job in jobs) - { - if (job is ContainerParentJob && !cpjs.Contains(job)) - { - cpjs.Add(job); - continue; - } - - PSWorkflowJob wfj = job as PSWorkflowJob; - Dbg.Assert(wfj != null, "if it's not a containerparentjob, it had better be a workflowjob"); - ContainerParentJob cpj = _jobRepository.GetItem((Guid)wfj.JobMetadata[Constants.JobMetadataParentInstanceId]); - if (!cpjs.Contains(cpj)) - { - cpjs.Add(cpj); - } - } - - return cpjs; - } - - /// - /// Remove a job from the store - /// - /// job object to remove - public override void RemoveJob(Job2 job) - { - if (job == null) - { - throw new ArgumentNullException("job"); - } - _structuredTracer.RemoveJobStarted(job.InstanceId); - - var jobInMemory = _jobRepository.GetItem(job.InstanceId); - if (jobInMemory == null) - { - // the specified job is not available in memory - // load the job repository - PopulateJobRepositoryIfRequired(); - } - - if (!(job is ContainerParentJob)) - { - throw new InvalidOperationException(Resources.CannotRemoveWorkflowJobDirectly); - } - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Removing Workflow job with instance id: {0}", job.InstanceId)); - - Exception innerException = null; - - foreach (Job childJob in job.ChildJobs) - { - PSWorkflowJob workflowChildJob = childJob as PSWorkflowJob; - - if (workflowChildJob == null) continue; - - try - { - GetJobManager().RemoveJob(workflowChildJob.InstanceId); - _structuredTracer.JobRemoved(job.InstanceId, - childJob.InstanceId, workflowChildJob.WorkflowGuid); - } - catch (ArgumentException exception) - { - //ignoring the error message and just logging them into ETW - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Ignoring the exception. Exception details: {0}", - exception)); - innerException = exception; - _structuredTracer.JobRemoveError(job.InstanceId, - childJob.InstanceId, workflowChildJob.WorkflowGuid, - exception.Message); - } - } - - // remove the container parent job from repository - try - { - _jobRepository.Remove((ContainerParentJob) job); - } - catch(ArgumentException exception) - { - //ignoring the error message and just logging them into ETW - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Ignoring the exception. Exception details: {0}", - exception)); - innerException = exception; - } - job.Dispose(); - - // Failed to remove the job? - if (innerException != null) - { - ArgumentException exc = new ArgumentException(Resources.WorkflowChildCouldNotBeRemoved, "job", innerException); - throw exc; - } - } - - // in case of multiple child jobs synchronization is very important - private readonly object _syncRemoveChilJob = new object(); - internal void RemoveChildJob(Job2 childWorkflowJob) - { - _structuredTracer.RemoveJobStarted(childWorkflowJob.InstanceId); - PopulateJobRepositoryIfRequired(); - - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Removing Workflow job with instance id: {0}", childWorkflowJob.InstanceId)); - - lock (_syncRemoveChilJob) - { - PSWorkflowJob childJob = childWorkflowJob as PSWorkflowJob; - if (childJob == null) return; - - object data; - PSWorkflowInstance instance = childJob.PSWorkflowInstance; - - if (!instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataParentInstanceId, out data)) return; - var parentInstanceId = (Guid)data; - ContainerParentJob job = _jobRepository.GetItem(parentInstanceId); - - job.ChildJobs.Remove(childJob); - - - try - { - GetJobManager().RemoveJob(childJob.InstanceId); - _structuredTracer.JobRemoved(job.InstanceId, - childJob.InstanceId, childJob.WorkflowGuid); - } - catch (ArgumentException exception) - { - //ignoring the error message and just logging them into ETW - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Ignoring the exception. Exception details: {0}", - exception)); - - _structuredTracer.JobRemoveError(job.InstanceId, - childJob.InstanceId, childJob.WorkflowGuid, - exception.Message); - } - - if (job.ChildJobs.Count == 0) - { - // remove the container parent job from repository - try - { - _jobRepository.Remove(job); - } - catch (ArgumentException exception) - { - //ignoring the error message and just logging them into ETW - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "WorkflowJobSourceAdapter: Ignoring the exception. Exception details: {0}", - exception)); - } - job.Dispose(); - } - } - } - - #endregion - - #region Private Methods - - private Activity ValidateWorkflow(JobInvocationInfo invocationInfo, Activity activity, ref bool? isSuspendable) - { - PSWorkflowRuntime runtime = PSWorkflowRuntime.Instance; - JobDefinition definition = invocationInfo.Definition; - WorkflowJobDefinition workflowJobDefinition = WorkflowJobDefinition.AsWorkflowJobDefinition(definition); - - bool externalActivity = true; - - if (activity == null) - { - bool windowsWorkflow; - activity = DefinitionCache.Instance.GetActivityFromCache(workflowJobDefinition, out windowsWorkflow); - // Debug.Assert(activity != null, "WorkflowManager failed validation for a null activity"); - if (activity == null) - { - throw new InvalidOperationException(); - } - - externalActivity = false; - - // skip validation if it is a windows workflow - if (windowsWorkflow) - return activity; - } - - // If validation is disabled, just return the activity as is... - if (!runtime.Configuration.EnableValidation) - { - return activity; - } - - if (externalActivity && !DefinitionCache.Instance.AllowExternalActivity) - { - // If there is a custom validator activity, then call it. If it returns true - // then just return the activity. - if (Validation.CustomHandler == null) - throw new InvalidOperationException(); - - if (Validation.CustomHandler.Invoke(activity)) - { - return activity; - } - - string displayName = activity.DisplayName; - - if (string.IsNullOrEmpty(displayName)) - displayName = this.GetType().Name; - - string message = string.Format(CultureInfo.CurrentCulture, Resources.InvalidActivity, displayName); - throw new ValidationException(message); - } - PSWorkflowValidationResults validationResults = GetWorkflowValidator().ValidateWorkflow( - definition.InstanceId, activity, - DefinitionCache.Instance.GetRuntimeAssemblyName(workflowJobDefinition)); - - if (validationResults.Results != null) - { - GetWorkflowValidator().ProcessValidationResults(validationResults.Results); - } - - isSuspendable = validationResults.IsWorkflowSuspendable; - - return activity; - } - - /// - /// Searches a list of jobs with a given set of filters for all - /// the V2 search parameters. This function searches in a specific - /// order so that a get call from an API returns without having - /// to do much processing in terms of wildcard pattern matching - /// - /// incoming enumeration of jobs to - /// search - /// dictionary of filters to use as search - /// criteria - /// narrowed down list of jobs that satisfy the filter - /// criteria - internal static List SearchJobsOnV2Parameters(IEnumerable jobsToSearch, IDictionary filter) - { - List searchList = new List(); - searchList.AddRange(jobsToSearch); - - List newlist; - if (filter.ContainsKey(Constants.JobMetadataSessionId)) - { - int searchId = (int)filter[Constants.JobMetadataSessionId]; - newlist = searchList.Where(job => job.Id == searchId).ToList(); - searchList.Clear(); - searchList = newlist; - } - if (filter.ContainsKey(Constants.JobMetadataInstanceId)) - { - var value = filter[Constants.JobMetadataInstanceId]; - Guid searchGuid; - LanguagePrimitives.TryConvertTo(value, CultureInfo.InvariantCulture, out searchGuid); - newlist = searchList.Where(job => job.InstanceId == searchGuid).ToList(); - searchList.Clear(); - searchList = newlist; - } - if (filter.ContainsKey(Constants.JobMetadataFilterState)) - { - JobState searchState = - (JobState) - LanguagePrimitives.ConvertTo(filter[Constants.JobMetadataFilterState], typeof(JobState), CultureInfo.InvariantCulture); - newlist = searchList.Where(job => job.JobStateInfo.State == searchState).ToList(); - searchList.Clear(); - searchList = newlist; - } - if (filter.ContainsKey(Constants.JobMetadataName)) - { - Debug.Assert(filter[Constants.JobMetadataName] is string || - filter[Constants.JobMetadataName] is WildcardPattern, "filter value should be a string or wildcard"); - - WildcardPattern patternForName; - if (filter[Constants.JobMetadataName] is string) - { - string name = (string)filter[Constants.JobMetadataName]; - patternForName = new WildcardPattern(name, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - } - else - patternForName = (WildcardPattern) filter[Constants.JobMetadataName]; - - newlist = - searchList.Where(parentJob => patternForName.IsMatch(parentJob.Name)).ToList(); - searchList.Clear(); - searchList = newlist; - } - if (filter.ContainsKey(Constants.JobMetadataCommand)) - { - Debug.Assert(filter[Constants.JobMetadataCommand] is string || - filter[Constants.JobMetadataCommand] is WildcardPattern, "filter value should be a string or wildcard"); - - WildcardPattern patternForCommand; - if (filter[Constants.JobMetadataCommand] is string) - { - string command = (string)filter[Constants.JobMetadataCommand]; - patternForCommand = new WildcardPattern(command, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - } - else - patternForCommand = (WildcardPattern)filter[Constants.JobMetadataCommand]; - - newlist = - searchList.Where(parentJob => patternForCommand.IsMatch(parentJob.Command)).ToList(); - searchList.Clear(); - searchList = newlist; - } - return searchList; - } - - private void PopulateJobRepositoryIfRequired() - { - // we need to populate the repository the first time - if (_repositoryPopulated) return; - lock (_syncObject) - { - if (_repositoryPopulated) return; - - _repositoryPopulated = true; - - LoadWorkflowInstancesFromStore(); - - // start the workflow manager - this will either - // create the store if it doesn't exist or load - // existing workflows - foreach (var job in CreateJobsFromWorkflows(GetJobManager().GetJobs(), true)) - { - try - { - _jobRepository.Add((ContainerParentJob) job); - } - catch(ArgumentException) - { - // if the job was created using NewJob() then - // the repository already has it and an exception - // will be thrown. Ignore the exception in this case - } - } - } - } - - private ICollection GetChildJobsFromRepository() - { - return _jobRepository.GetItems().SelectMany(parentJob => parentJob.ChildJobs).Cast().ToList(); - } - - private IEnumerable CreateJobsFromWorkflows(IEnumerable workflowJobs, bool returnParents) - { - // Jobs in this collection correspond to the ContainerParentJob objects. PSWorkflowJob objects - // are children of these. - var reconstructedParentJobs = new Dictionary(); - var jobs = new List(); - - if (workflowJobs == null) return jobs; - - // If a workflow instance has incomplete metadata, we do not create the job for it. - foreach (var job in workflowJobs) - { - var wfjob = job as PSWorkflowJob; - Debug.Assert(wfjob != null, "Job supplied must be of type PSWorkflowJob"); - PSWorkflowInstance instance = wfjob.PSWorkflowInstance; - Dbg.Assert(instance != null, "PSWorkflowInstance should be reconstructed before attempting to rehydrate job"); - - if (!instance.JobStateRetrieved || instance.PSWorkflowContext.JobMetadata == null || instance.PSWorkflowContext.JobMetadata.Count == 0) continue; - - object data; - string name, command; - Guid instanceId; - if (!GetJobInfoFromMetadata(instance, out command, out name, out instanceId)) continue; - - if (!instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataParentInstanceId, out data)) - continue; - var parentInstanceId = (Guid)data; - - // If the parent job is needed, find or create it now so that the ID is sequentially lower. - if (returnParents && !reconstructedParentJobs.ContainsKey(parentInstanceId)) - { - if (!instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataParentName, out data)) - continue; - var parentName = (string)data; - - if (!instance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataParentCommand, out data)) - continue; - var parentCommand = (string)data; - - JobIdentifier parentId = RetrieveJobIdForReuse(parentInstanceId); - ContainerParentJob parentJob = parentId != null - ? new ContainerParentJob(parentCommand, parentName, parentId, AdapterTypeName) - : new ContainerParentJob(parentCommand, parentName, parentInstanceId, AdapterTypeName); - - // update job metadata with new parent session Id--needed for filtering. - // The pid in the metadata has already been updated at this point. - Dbg.Assert( - instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentSessionId), - "Job Metadata for instance incomplete."); - if (instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataParentSessionId)) - instance.PSWorkflowContext.JobMetadata[Constants.JobMetadataParentSessionId] = parentJob.Id; - - reconstructedParentJobs.Add(parentInstanceId, parentJob); - } - - // update job metadata with new session Id--needed for filtering. - Dbg.Assert(instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataSessionId), "Job Metadata for instance incomplete."); - Dbg.Assert(instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataPid), "Job Metadata for instance incomplete."); - if (instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataSessionId)) - instance.PSWorkflowContext.JobMetadata[Constants.JobMetadataSessionId] = job.Id; - if (instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.JobMetadataPid)) - instance.PSWorkflowContext.JobMetadata[Constants.JobMetadataPid] = Process.GetCurrentProcess().Id; - - job.StartParameters = new List(); - CommandParameterCollection commandParameterCollection = new CommandParameterCollection(); - AddStartParametersFromCollection(instance.PSWorkflowContext.WorkflowParameters, commandParameterCollection); - AddStartParametersFromCollection(instance.PSWorkflowContext.PSWorkflowCommonParameters, commandParameterCollection); - - bool takesPSPrivateMetadata; - if (instance.PSWorkflowContext.JobMetadata.ContainsKey(Constants.WorkflowTakesPrivateMetadata)) - { - takesPSPrivateMetadata = (bool)instance.PSWorkflowContext.JobMetadata[Constants.WorkflowTakesPrivateMetadata]; - } - else - { - DynamicActivity da = instance.PSWorkflowDefinition != null ? instance.PSWorkflowDefinition.Workflow as DynamicActivity : null; - takesPSPrivateMetadata = da != null && da.Properties.Contains(Constants.PrivateMetadata); - } - - // If there is Private Metadata and it is not included in the "Input" collection, add it now. - if (instance.PSWorkflowContext.PrivateMetadata != null - && instance.PSWorkflowContext.PrivateMetadata.Count > 0 - && !takesPSPrivateMetadata) - { - Hashtable privateMetadata = new Hashtable(); - foreach (var pair in instance.PSWorkflowContext.PrivateMetadata) - { - privateMetadata.Add(pair.Key, pair.Value); - } - commandParameterCollection.Add(new CommandParameter(Constants.PrivateMetadata, privateMetadata)); - } - job.StartParameters.Add(commandParameterCollection); - - if (returnParents) - { - ((ContainerParentJob)reconstructedParentJobs[parentInstanceId]).AddChildJob(job); - } - else - { - jobs.Add(job); - } - - if (!wfjob.WorkflowInstanceLoaded) - { - // RestoreFromWorkflowInstance sets the job state. Because we've used AddChildJob, the parent's state will be - // updated automatically. - wfjob.RestoreFromWorkflowInstance(instance); - } - } - - if (returnParents) - { - jobs.AddRange(reconstructedParentJobs.Values); - } - - return jobs; - } - - /// - /// Handles the wsman server shutting down event. - /// - /// sender of this event - /// arguments describing the event - private void OnWSManServerShuttingDownEventRaised(object sender, EventArgs e) - { - try - { - IsShutdownInProgress = true; - PSWorkflowConfigurationProvider _configuration = (PSWorkflowConfigurationProvider)PSWorkflowRuntime.Instance.Configuration; - int timeout = _configuration.WorkflowShutdownTimeoutMSec; - - GetJobManager().ShutdownWorkflowManager(timeout); - } - catch (Exception exception) - { - - // when exception on shutdown, not much to do much other than logging the exception - _tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Shutting down WSMan server: Exception details: {0}", - exception)); - - Dbg.Assert(false, "Exception has happened during the shutdown API. [Message] " + exception.Message + "[StackTrace] " + exception.StackTrace); - - } - } - internal bool IsShutdownInProgress = false; - - private static void AddStartParametersFromCollection(Dictionary collection, CommandParameterCollection allParams) - { - if (collection == null || collection.Count <= 0) return; - foreach (var param in collection.Select(o => new CommandParameter(o.Key, o.Value))) - { - allParams.Add(param); - } - } - - #endregion Private Methods - - #region Constructors - - internal WorkflowJobSourceAdapter() - { - WSManServerChannelEvents.ShuttingDown += OnWSManServerShuttingDownEventRaised; - Name = AdapterTypeName; - } - - #endregion Constructors - - #region Internal Methods - - // called from test code to clean up - internal void ClearRepository() - { - foreach (var item in _jobRepository.GetItems()) - { - _jobRepository.Remove(item); - } - } - - internal static bool GetJobInfoFromMetadata(PSWorkflowInstance workflowInstance, out string command, out string name, out Guid instanceId) - { - bool retVal = false; - command = string.Empty; - name = string.Empty; - instanceId = Guid.Empty; - - do - { - object data; - if (!workflowInstance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataName, out data)) break; - name = (string)data; - - if (!workflowInstance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataCommand, out data)) break; - command = (string)data; - - if (!workflowInstance.PSWorkflowContext.JobMetadata.TryGetValue(Constants.JobMetadataInstanceId, out data)) break; - instanceId = (Guid)data; - - retVal = true; - } while (false); - - return retVal; - } - - private bool _fullyLoaded; - internal void LoadWorkflowInstancesFromStore() - { - if (_fullyLoaded) return; - lock (_syncObject) - { - if (_fullyLoaded) return; - - foreach (PSWorkflowId storedInstanceId in PSWorkflowFileInstanceStore.GetAllWorkflowInstanceIds()) - { - try - { - GetJobManager().LoadJobWithIdentifier(storedInstanceId); - } - catch (Exception e) - { - // Auto loading failing so logging into the message trace - - _tracer.WriteMessage("Getting an exception while loading the previously persisted workflows..."); - _tracer.TraceException(e); - // Intentionally continuing on with exception - } - } - - _fullyLoaded = true; - } - - GetJobManager().CleanUpWorkflowJobTable(); - } - - /// - /// Called from tests to cleanup instance table - /// - internal void ClearWorkflowTable() - { - // First clean up existing jobs - GetJobManager().ClearWorkflowManagerInstanceTable(); - - // Now force load all the jobs from disk - _fullyLoaded = false; - LoadWorkflowInstancesFromStore(); - - // Now clean up the stuff loaded from disk - GetJobManager().ClearWorkflowManagerInstanceTable(); - - } - - #endregion Internal Methods - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowManager.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowManager.cs deleted file mode 100644 index 77c9831f08a..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowManager.cs +++ /dev/null @@ -1,1482 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Activities.Validation; -using System.Collections.ObjectModel; -using System.Linq; -using System.Management.Automation.Remoting; -using System.Management.Automation.Remoting.WSMan; -using System.Runtime.InteropServices; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Activities; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Diagnostics; - using System.Management.Automation; - using System.Management.Automation.Tracing; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Management.Automation.Runspaces; - using System.Management.Automation.PerformanceData; - using System.Threading; - using Timer = System.Timers.Timer; - - /// - /// Provides interface to upper layers of M3P for calling the Workflow core functionality. - /// Throttles the number of jobs run simultaneously. Used to control the number of workflows that will execute simultaneously - /// - public sealed class PSWorkflowJobManager: IDisposable - { - #region Private Members - - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer StructuredTracer = new Tracer(); - - private readonly PSWorkflowRuntime _runtime; - /// - /// The class name. - /// - private const string Facility = "WorkflowManager : "; - private LockObjectsCollection lockObjects = new LockObjectsCollection(); - - #endregion Private Members - - #region Job Throttle related Private Members - - private readonly ConcurrentQueue, object, JobState>> _pendingQueue = new ConcurrentQueue, object, JobState>>(); - private static readonly PSPerfCountersMgr PerfCountersMgr = PSPerfCountersMgr.Instance; - private readonly int _throttleLimit; - private int _inProgressCount; - - private const int WaitInterval = 5 * 1000; - - // Timer to call WSManPluginOperationComplete on zero active sessions and no jobs are inprogress or in pending queue - private Timer _shutdownTimer; - - // Plugin/Endpoint process is created during the first shell session creation, so the default value of _activeSessionsCount is 1 - private int _activeSessionsCount = 1; - - /// - /// this is set to a date in the past so that the first time - /// GC happens correctly - /// - private static DateTime _lastGcTime = new DateTime(2011, 1, 1); - private const int GcDelayInMinutes = 5; - private static int _workflowsBeforeGc; - private static int _gcStatus = NotInProgress; - private const int InProgress = 1; - private const int NotInProgress = 0; - private static readonly Tracer etwTracer = new Tracer(); - - /// - /// if these many workflows are run without a GC - /// in between them, then we will force a GC - /// - private const int WorkflowLimitBeforeGc = 25 * 5; - - #endregion Job Throttle related Private Members - - // For testing purpose ONLY - internal static bool TestMode = false; - // For testing purpose ONLY - internal static long ObjectCounter = 0; - - private bool _isDisposed; - private bool _isDisposing; - private readonly object _syncObject = new object(); - - #region WorkflowManager Management - - /// - /// Construct a workflow manager - /// - /// - /// - public PSWorkflowJobManager(PSWorkflowRuntime runtime, int throttleLimit) - { - if (runtime == null) - { - throw new ArgumentNullException("runtime"); - } - - if (TestMode) - { - System.Threading.Interlocked.Increment(ref ObjectCounter); - } - - _runtime = runtime; - _throttleLimit = throttleLimit; - - if (PSWorkflowSessionConfiguration.IsWorkflowTypeEndpoint) - { - _shutdownTimer = new Timer(_runtime.Configuration.WSManPluginReportCompletionOnZeroActiveSessionsWaitIntervalMSec); - _shutdownTimer.Elapsed += ShutdownWaitTimerElapsed; - _shutdownTimer.AutoReset = false; - - WSManServerChannelEvents.ActiveSessionsChanged += OnWSManServerActiveSessionsChangedEvent; - } - } - - - /// - /// Dispose implementation. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose implementation. - /// - /// - private void Dispose(bool disposing) - { - if (_isDisposed || !disposing) - return; - - lock (_syncObject) - { - if (_isDisposed) - return; - - _isDisposing = true; - - // To release the job throttling thread - _waitForJobs.Value.Set(); - - ShutdownWorkflowManager(); - - // Set _waitForJobs to free the service thread - _waitForJobs.Value.Dispose(); - - if (PSWorkflowSessionConfiguration.IsWorkflowTypeEndpoint) - { - WSManServerChannelEvents.ActiveSessionsChanged -= OnWSManServerActiveSessionsChangedEvent; - } - - if (_shutdownTimer != null) - { - _shutdownTimer.Elapsed -= ShutdownWaitTimerElapsed; - _shutdownTimer.Close(); - _shutdownTimer.Dispose(); - _shutdownTimer = null; - } - - _isDisposed = true; - } - } - - private void AssertNotDisposed() - { - if (_isDisposed) - { - throw new ObjectDisposedException("PSWorkflowJobManager"); - } - } - - /// - /// LoadJob - /// - /// - /// - public PSWorkflowJob LoadJob(PSWorkflowId storedInstanceId) - { - AssertNotDisposed(); - - if (storedInstanceId == null) throw new ArgumentNullException("storedInstanceId"); - - if(LoadJobWithIdentifier(storedInstanceId)) - { - return Get(null, storedInstanceId.Guid); - } - - return null; - } - - internal bool LoadJobWithIdentifier(PSWorkflowId storedInstanceId) - { - PSWorkflowInstance instance = _runtime.Configuration.CreatePSWorkflowInstance(storedInstanceId); - - try - { - instance.InstanceStore.Load(WorkflowStoreComponents.JobState | WorkflowStoreComponents.Metadata); - } - catch (Exception e) - { - Tracer.TraceException(e); - - instance.JobStateRetrieved = false; - instance.PSWorkflowContext = new PSWorkflowContext(); - } - - if (!instance.JobStateRetrieved) - { - instance.RemoveInstance(); - return false; - } - - string command; - string name; - Guid instanceId; - if (!WorkflowJobSourceAdapter.GetJobInfoFromMetadata(instance, out command, out name, out instanceId)) - { - instance.RemoveInstance(); - return false; - } - - if (instance.Timer != null) - { - if (instance.Timer.CheckIfTimerHasReachedAlready(WorkflowTimerType.ElapsedTimer)) - { - instance.RemoveInstance(); - return false; - } - - // starting the elapsed timer immediately. - instance.Timer.StartTimer(WorkflowTimerType.ElapsedTimer); - } - - if (_wfJobTable.ContainsKey(instanceId)) - { - return true; - } - - lock (lockObjects.GetLockObject(instanceId)) - { - if (_wfJobTable.ContainsKey(instanceId)) - { - return true; - } - - PSWorkflowJob newjob = new PSWorkflowJob(_runtime, command, name, instanceId); - newjob.PSWorkflowInstance = instance; - instance.PSWorkflowJob = newjob; - newjob.RestoreFromWorkflowInstance(instance); - newjob.WorkflowInstanceLoaded = true; - newjob.ConfigureWorkflowHandlers(); - if (!_wfJobTable.ContainsKey(newjob.InstanceId)) - { - AddJob(newjob); - } - return true; - } - } - - private readonly object _servicingThreadSyncObject = new object(); - private bool _needToStartServicingThread = true; - - /// - /// Indicates whether the dedicated thread to start jobs is required - /// The thread needs to started only the first time - /// - /// This property helps in delay creation of - /// the thread - private bool NeedToStartServicingThread - { - get - { - if (_needToStartServicingThread) - { - lock(_servicingThreadSyncObject) - { - if (_needToStartServicingThread) - { - _needToStartServicingThread = false; - return true; - } - } - } - return false; - } - } - - private readonly Lazy _waitForJobs = new Lazy(() => new AutoResetEvent(false)); - - private void CheckAndStartServicingThread() - { - // check to see if waking the servicing thread at - // this point will actually lead to servicing. If - // so wake it up - if (_inProgressCount < _throttleLimit && _pendingQueue.Count > 0) - _waitForJobs.Value.Set(); - - if (PSWorkflowSessionConfiguration.IsWorkflowTypeEndpoint) - { - lock (_syncObject) - { - // Start the shutdown timer when there is no inprogress job, no pending job requests and no active sessions - // Otherwise disable it - if (_activeSessionsCount == 0 && _inProgressCount == 0 && _pendingQueue.Count == 0) - { - _shutdownTimer.Enabled = true; - } - else - { - _shutdownTimer.Enabled = false; - } - } - } - - if (!NeedToStartServicingThread) return; - - Thread servicingThread = new Thread(StartOperationsFromQueue) - {Name = "Job Throttling Thread"}; - servicingThread.IsBackground = true; - servicingThread.Start(); - } - - private void WaitTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) - { - Timer waitTimer = sender as Timer; - Debug.Assert(waitTimer != null, "Sender was not send correctly"); - - if (_inProgressCount == 0 && _pendingQueue.Count == 0) - { - // at the end of wait time if there are no jobs - // then run garbage collection. This need not be - // perfectly thread safe hence these lockless - // checks - to minimize overhead - RunGarbageCollection(false); - } - - // cleanup - waitTimer.Elapsed -= WaitTimerElapsed; - waitTimer.Dispose(); - } - - /// - /// PerformWSManPluginReportCompletion - /// - [DllImport("pwrshplugin.dll", EntryPoint = "PerformWSManPluginReportCompletion", SetLastError = false)] - private static extern void PerformWSManPluginReportCompletion(); - - private void ShutdownWaitTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) - { - Timer waitTimer = sender as Timer; - Debug.Assert(waitTimer != null, "Sender was not send correctly"); - Debug.Assert(waitTimer.Equals(_shutdownTimer), "Sender was not send correctly"); - - bool callWSManPluginReportCompletion = false; - - lock(_syncObject) - { - if (_activeSessionsCount == 0 && _inProgressCount == 0 && _pendingQueue.Count == 0) - { - callWSManPluginReportCompletion = true; - } - } - - if(callWSManPluginReportCompletion) - { - Tracer.WriteMessage("Calling WSManPluginReportCompletion as there are no active sessions and no inprogress/pending jobs"); - // Endpoint will not be shutdown if there is any active session - PerformWSManPluginReportCompletion(); - } - } - - /// - /// Handles the wsman server shutting down event. - /// - /// sender of this event - /// arguments describing the event - private void OnWSManServerActiveSessionsChangedEvent(object sender, ActiveSessionsChangedEventArgs eventArgs) - { - if (eventArgs != null) - { - lock (_syncObject) - { - _activeSessionsCount = eventArgs.ActiveSessionsCount; - - if (_shutdownTimer != null) - { - // Start the shutdown timer when there is no inprogress job, no pending job requests and no active sessions - // Otherwise disable it - if (_activeSessionsCount == 0 && _inProgressCount == 0 && _pendingQueue.Count == 0) - { - _shutdownTimer.Enabled = true; - } - else - { - _shutdownTimer.Enabled = false; - } - } - } - } - } - - /// - /// Contains the logic for running garbage collection - /// - /// if true, time check will not be - /// done and a forced garbage collection will take place - /// This method ensures that garbage collection - /// runs from only one thread at a time - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] - private static void RunGarbageCollection(bool force) - { - if (Interlocked.CompareExchange(ref _gcStatus, InProgress, NotInProgress) != NotInProgress) - return; - - // if GC is run based on time elapsed, then we want atleast a 5 minute gap between 2 GC runs - if (force || DateTime.Compare(DateTime.Now, _lastGcTime.AddMinutes(GcDelayInMinutes)) >= 0) - { - _lastGcTime = DateTime.Now; - Interlocked.Exchange(ref _workflowsBeforeGc, 0); - - etwTracer.BeginRunGarbageCollection(); - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - etwTracer.EndRunGarbageCollection(); - } - - Interlocked.CompareExchange(ref _gcStatus, NotInProgress, InProgress); - } - - private void StartOperationsFromQueue() - { - do - { - Tuple, object, JobState> tuple; - while (_inProgressCount < _throttleLimit && _pendingQueue.TryDequeue(out tuple)) - { - Action operationDelegate = tuple.Item1; - - // decrement the current queue length perf counter - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.WaitingWorkflowJobsCount, - -1); - - - var wfJob = tuple.Item1.Target as PSWorkflowJob; - Debug.Assert(wfJob != null, "Target cannot be null"); - - // Increment in progress job count only when OnJobStateChanged Event handler is registered. - // Event handler will be added if job's current is state is either expected state or running. - // OnJobStateChanged handler decrements the in progress job count when job is Suspended, Completed, Failed or Stopped. - // - if (wfJob.CheckAndAddStateChangedEventHandler(OnJobStateChanged, tuple.Item3)) - Interlocked.Increment(ref _inProgressCount); - - // increment the current running workflow jobs counter - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.RunningWorkflowJobsCount); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.RunningWorkflowJobsPerSec); - operationDelegate(tuple.Item2); - operationDelegate = null; - tuple = null; - } - - // there are no more requests to service however there - // can be running jobs. When everything is complete and - // _inProgressCount is 0 and there no further requests, - // this method will still be called. At that point fire - // the wait timer - if (_inProgressCount == 0 && _pendingQueue.Count == 0) - { - Timer waitTimer = new Timer(); - - waitTimer.Elapsed += WaitTimerElapsed; - waitTimer.Interval = WaitInterval; - waitTimer.AutoReset = false; - waitTimer.Enabled = true; - } - - try - { - // we have reached this point either because there are - // no pending jobs or because we have hit the number of - // jobs that can be currently run. This thread will now - // sleep until thawed by an appropriate action - _waitForJobs.Value.WaitOne(); - } - catch (ObjectDisposedException) - { - // ObjectDisposedException will be thrown when _waitForJobs.Value is disposed. - } - } while (!_isDisposed && !_isDisposing); - } - - private void OnJobStateChanged(object sender, JobStateEventArgs eventArgs) - { - switch (eventArgs.JobStateInfo.State) - { - case JobState.Suspended: - case JobState.Stopped: - case JobState.Failed: - case JobState.Completed: - { - Job2 job = sender as Job2; - Debug.Assert(job != null, "Job instance need to passed when StateChanged event is raised"); - job.StateChanged -= OnJobStateChanged; - - // start more operations from queue if required - Interlocked.Decrement(ref _inProgressCount); - // decrement the workflow jobs being serviced perf counter - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.RunningWorkflowJobsCount, - -1); - - if (Interlocked.Increment(ref _workflowsBeforeGc) >= WorkflowLimitBeforeGc) - { - RunGarbageCollection(true); - } - - CheckAndStartServicingThread(); - } - break; - } - } - - /// - /// SubmitOperation - /// - /// - /// - /// - /// - internal void SubmitOperation(Job2 job, Action operationHandler, object state, JobState expectedState) - { - Debug.Assert(job != null, "Null job passed"); - Debug.Assert(operationHandler != null, "Null delegate passed"); - - if (job == null) throw new ArgumentNullException("job"); - - // Adding OnJobStateChanged event handler here caused negative _inProgressCount when jobs are stopped before they got picked up by servicing thread. - _pendingQueue.Enqueue(Tuple.Create(operationHandler, state, expectedState)); - PerfCountersMgr.UpdateCounterByValue( - PSWorkflowPerformanceCounterSetInfo.CounterSetId, - PSWorkflowPerformanceCounterIds.WaitingWorkflowJobsCount); - CheckAndStartServicingThread(); - } - - /// - /// Create a workflow job by providing the activity-tree representing the workflow. - /// - /// - /// - /// - /// - /// - /// - public PSWorkflowJob CreateJob(Guid jobInstanceId, Activity workflow, string command, string name, Dictionary parameters) - { - return CreateJob(jobInstanceId, workflow, command, name, parameters, null); - } - - /// - /// Create a workflow job by providing the xaml representing the workflow. - /// - /// - /// - /// - /// - /// - /// - public PSWorkflowJob CreateJob(Guid jobInstanceId, string workflowXaml, string command, string name, Dictionary parameters) - { - return CreateJob(jobInstanceId, workflowXaml, command, name, parameters, null); - } - - /// - /// Create a workflow job by providing the activity-tree representing the workflow. - /// - /// - /// - /// - /// - /// - /// - /// - public PSWorkflowJob CreateJob(Guid jobInstanceId, Activity workflow, string command, string name, Dictionary parameters, Dictionary creationContext) - { - return CreateJobInternal(jobInstanceId, workflow, command, name, parameters, null, creationContext); - } - - /// - /// Create a workflow job by providing the xaml representing the workflow. - /// - /// - /// - /// - /// - /// - /// - /// - public PSWorkflowJob CreateJob(Guid jobInstanceId, string workflowXaml, string command, string name, Dictionary parameters, Dictionary creationContext) - { - Activity workflow = Microsoft.PowerShell.Commands.ImportWorkflowCommand.ConvertXamlToActivity(workflowXaml); - return CreateJobInternal(jobInstanceId, workflow, command, name, parameters, workflowXaml, creationContext); - } - - internal PSWorkflowJob CreateJobInternal(Guid jobInstanceId, Activity workflow, string command, string name, Dictionary parameters, string xaml, Dictionary creationContext) - { - AssertNotDisposed(); - - if (jobInstanceId == Guid.Empty) - throw new ArgumentNullException("jobInstanceId"); - - if (workflow == null) - throw new ArgumentNullException("workflow"); - - if (command == null) - throw new ArgumentNullException("command"); - - if (name == null) - throw new ArgumentNullException("name"); - - if (_wfJobTable.ContainsKey(jobInstanceId)) - { - ArgumentException exception = new ArgumentException(Resources.DuplicateInstanceId); - Tracer.TraceException(exception); - throw exception; - } - - lock (lockObjects.GetLockObject(jobInstanceId)) - { - - if (_wfJobTable.ContainsKey(jobInstanceId)) - { - ArgumentException exception = new ArgumentException(Resources.DuplicateInstanceId); - Tracer.TraceException(exception); - throw exception; - } - - JobDefinition definition = new JobDefinition(typeof(WorkflowJobSourceAdapter), command, name); - - var parameterDictionary = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - - if (parameters != null) - { - foreach (KeyValuePair param in parameters) - { - parameterDictionary.Add(param.Key, param.Value); - } - } - - string[] computerNames = null; - bool gotComputerNames = false; - object value; - if (parameterDictionary.Count != 0 && parameterDictionary.TryGetValue(Constants.ComputerName, out value)) - { - if (LanguagePrimitives.TryConvertTo(value, CultureInfo.InvariantCulture, out computerNames)) - gotComputerNames = computerNames != null; - } - - if (gotComputerNames) - { - if (computerNames.Length > 1) - throw new ArgumentException(Resources.OneComputerNameAllowed); - - parameterDictionary.Remove(Constants.ComputerName); - } - - var childSpecification = new JobInvocationInfo(definition, parameterDictionary); - childSpecification.Command = command; - - // If actual computernames were specified, then set the PSComputerName parameter. - if (gotComputerNames) - { - var computerNameParameter = new CommandParameter(Constants.ComputerName, computerNames); - childSpecification.Parameters[0].Add(computerNameParameter); - } - - // Job objects will be disposed of on parent job removal. - var childJob = new PSWorkflowJob(_runtime, childSpecification, jobInstanceId, creationContext); - childJob.JobMetadata = CreateJobMetadataWithNoParentDefined(childJob, computerNames); - - childJob.LoadWorkflow(childSpecification.Parameters[0], workflow, xaml); - this.AddJob(childJob); - - return childJob; - } - } - - private static Dictionary CreateJobMetadataWithNoParentDefined(Job job, string[] parentComputers) - { - var jobMetadata = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - {Constants.JobMetadataInstanceId, job.InstanceId}, - {Constants.JobMetadataSessionId, job.Id}, - {Constants.JobMetadataName, job.Name}, - {Constants.JobMetadataCommand, job.Command}, - {Constants.JobMetadataStateReason, job.JobStateInfo.Reason}, - {Constants.JobMetadataStatusMessage, job.StatusMessage}, - {Constants.JobMetadataUserName, Environment.UserName}, - {Constants.JobMetadataPid, CurrentProcessId}, - // This is needed because the jobs created by runtime APIs are not visible from the PS Console - // although they were using default store, because they don't have the parent metadata. - // Afer assigning the parent metadata these jobs will be visible from console if APIs are using - // default store. - {Constants.JobMetadataParentInstanceId, Guid.NewGuid()}, - {Constants.JobMetadataParentSessionId, 0}, // no need to worry about '0' because it will be re assigned when loaded into the session. - {Constants.JobMetadataParentName, job.Name}, - {Constants.JobMetadataParentCommand, job.Command} - }; - - return jobMetadata; - } - - - /// - /// CreateJob - /// - /// - /// - /// - internal ContainerParentJob CreateJob(JobInvocationInfo jobInvocationInfo, Activity activity) - { - if (jobInvocationInfo == null) - throw new ArgumentNullException("jobInvocationInfo"); - - if (jobInvocationInfo.Definition == null) - throw new ArgumentException(Resources.NewJobDefinitionNull, "jobInvocationInfo"); - - if (jobInvocationInfo.Command == null) - throw new ArgumentException(Resources.NewJobDefinitionNull, "jobInvocationInfo"); - - DynamicActivity dynamicActivity = activity as DynamicActivity; - - Debug.Assert(dynamicActivity != null, "Passed workflow must be a DynamicActivity"); - - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "WorkflowJobSourceAdapter: Creating Workflow job with definition: {0}", jobInvocationInfo.Definition.InstanceId)); - - // Create parent job. All PSWorkflowJob objects will be a child of some ContainerParentJob - // This job will be disposed of when RemoveJob is called. - ContainerParentJob newJob = new ContainerParentJob( - jobInvocationInfo.Command, - jobInvocationInfo.Name, - WorkflowJobSourceAdapter.AdapterTypeName); - - foreach (CommandParameterCollection commandParameterCollection in jobInvocationInfo.Parameters) - { - var parameterDictionary = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - foreach (CommandParameter param in commandParameterCollection) - { - parameterDictionary.Add(param.Name, param.Value); - } - - string[] computerNames = null; - bool gotComputerNames = false; - object value; - if (parameterDictionary.Count != 0 && parameterDictionary.TryGetValue(Constants.ComputerName, out value)) - { - if (LanguagePrimitives.TryConvertTo(value, CultureInfo.InvariantCulture, out computerNames)) - gotComputerNames = computerNames != null; - } - - StructuredTracer.ParentJobCreated(newJob.InstanceId); - - bool isComputerNameExists = false; - - if (dynamicActivity != null && dynamicActivity.Properties.Any(x => x.Name.Equals(Constants.ComputerName, StringComparison.CurrentCultureIgnoreCase))) - { - isComputerNameExists = true; - } - - dynamicActivity = null; - - if (isComputerNameExists) - { - var childSpecification = new JobInvocationInfo(jobInvocationInfo.Definition, parameterDictionary); - childSpecification.Command = newJob.Command; - - // If actual computernames were specified, then set the PSComputerName parameter. - if (gotComputerNames) - { - var computerNameParameter = new CommandParameter(Constants.ComputerName, computerNames); - childSpecification.Parameters[0].Add(computerNameParameter); - } - - // Job objects will be disposed of on parent job removal. - var childJob = new PSWorkflowJob(_runtime, childSpecification); - childJob.JobMetadata = CreateJobMetadata(childJob, newJob.InstanceId, newJob.Id, newJob.Name, newJob.Command, computerNames); - - childJob.LoadWorkflow(commandParameterCollection, activity, null); - this.AddJob(childJob); - newJob.AddChildJob(childJob); - StructuredTracer.ChildWorkflowJobAddition(childJob.InstanceId, newJob.InstanceId); - StructuredTracer.WorkflowJobCreated(newJob.InstanceId, childJob.InstanceId, childJob.WorkflowGuid); - } - else - { - // Remove array of computerNames from collection. - parameterDictionary.Remove(Constants.ComputerName); - - if (gotComputerNames) - { - foreach (var computerName in computerNames) - { - CreateChildJob(jobInvocationInfo, activity, newJob, commandParameterCollection, parameterDictionary, computerName, computerNames); - } - } - else - { - CreateChildJob(jobInvocationInfo, activity, newJob, commandParameterCollection, parameterDictionary, null, computerNames); - } - } - } - - StructuredTracer.JobCreationComplete(newJob.InstanceId, jobInvocationInfo.InstanceId); - Tracer.TraceJob(newJob); - - return newJob; - } - - private void CreateChildJob(JobInvocationInfo specification, Activity activity, ContainerParentJob newJob, CommandParameterCollection commandParameterCollection, Dictionary parameterDictionary, string computerName, string[] computerNames) - { - if (!string.IsNullOrEmpty(computerName)) - { - string[] childTargetComputerList = { computerName }; - - // Set the target computer for this child job... - parameterDictionary[Constants.ComputerName] = childTargetComputerList; - } - - var childSpecification = new JobInvocationInfo(specification.Definition, parameterDictionary); - - // Job objects will be disposed of on parent job removal. - var childJob = new PSWorkflowJob(_runtime, childSpecification); - childJob.JobMetadata = CreateJobMetadata(childJob, newJob.InstanceId, newJob.Id, newJob.Name, newJob.Command, computerNames); - - // Remove the parameter from the collection... - for (int index = 0; index < commandParameterCollection.Count; index++) - { - if (string.Equals(commandParameterCollection[index].Name, Constants.ComputerName, StringComparison.OrdinalIgnoreCase)) - { - commandParameterCollection.RemoveAt(index); - break; - } - } - - if (!string.IsNullOrEmpty(computerName)) - { - var computerNameParameter = new CommandParameter(Constants.ComputerName, computerName); - commandParameterCollection.Add(computerNameParameter); - } - - this.AddJob(childJob); - childJob.LoadWorkflow(commandParameterCollection, activity, null); - newJob.AddChildJob(childJob); - StructuredTracer.ChildWorkflowJobAddition(childJob.InstanceId, newJob.InstanceId); - Tracer.TraceJob(childJob); - StructuredTracer.WorkflowJobCreated(newJob.InstanceId, childJob.InstanceId, childJob.WorkflowGuid); - } - - private static readonly int CurrentProcessId = Process.GetCurrentProcess().Id; - private static Dictionary CreateJobMetadata(Job job, Guid parentInstanceId, int parentSessionId, - string parentName, string parentCommand, string[] parentComputers) - { - var jobMetadata = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - {Constants.JobMetadataInstanceId, job.InstanceId}, - {Constants.JobMetadataSessionId, job.Id}, - {Constants.JobMetadataName, job.Name}, - {Constants.JobMetadataCommand, job.Command}, - {Constants.JobMetadataStateReason, job.JobStateInfo.Reason}, - {Constants.JobMetadataStatusMessage, job.StatusMessage}, - {Constants.JobMetadataParentInstanceId, parentInstanceId}, - {Constants.JobMetadataParentSessionId, parentSessionId}, - {Constants.JobMetadataParentName, parentName}, - {Constants.JobMetadataParentCommand, parentCommand}, - {Constants.JobMetadataUserName, Environment.UserName}, - {Constants.JobMetadataPid, CurrentProcessId} - }; - - return jobMetadata; - } - - /// - /// ShutdownWorkflowManager is responsible suspending all the workflow and if suspend doesn't happen within timeout then calling the force suspend. - /// - /// The shutdown timeout in milliseconds. - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public void ShutdownWorkflowManager(int timeout = 500) - { - // Return if this is already disposed. - if (_isDisposed) - return; - - if (timeout <= 0) - throw new ArgumentException(Resources.ForceSuspendTimeout); - - - List forceHandles = new List(); - - foreach (var job in GetJobs()) - { - // non thread safe check to avoid straight forward exclusions - if (job.JobStateInfo.State == JobState.Running || job.JobStateInfo.State == JobState.Suspending) - { - try - { - if (job.DoAbortJob(Resources.ShutdownAbort)) // this will do the thread safe verification - forceHandles.Add(job.SuspendedOrAborted); - else - forceHandles.Add(job.Finished); - } - catch (Exception exception) - { - // To continue in aborting the remaining jobs, it is safe to discard all the exceptions during shutdown. - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, - "Shutting down workflow manager: suspend forcefully : Exception details: {0}", - exception)); - - } - } - } - - if (forceHandles.Count > 0) - { - WaitHandle.WaitAll(forceHandles.ToArray(), timeout); - forceHandles.Clear(); - } - - - // cleanup - foreach (var job in GetJobs()) - { - UnloadJob(job.InstanceId); - } - - _wfJobTable.Clear(); - } - - #endregion WorkflowManager Management - - #region Searching Workflows - - /// - /// Returns the workflow job currently loaded in memory with provided id. - /// This function DOES NOT try load the job from the store if not found in memory - /// - /// - /// - public PSWorkflowJob GetJob(Guid instanceId) - { - return Get(instanceId, null); - } - - /// - /// Loads and returns all workflow jobs. - /// - /// Returns the collection workflow jobs. - public IEnumerable GetJobs() - { - AssertNotDisposed(); - Tracer.WriteMessage(Facility + "Getting all the workflow instances."); - return _wfJobTable.Values; - } - - /// - /// Loads and returns all workflow jobs based on provided filters. - /// - /// Represent which type of data needs to be used to apply filter. - /// Filters represents the key value pair of conditions. - /// Returns the collection of workflow instances. - internal IEnumerable GetJobs(WorkflowFilterTypes type, Dictionary filters) - { - Tracer.WriteMessage(Facility + "Getting workflow instances based on filters"); - return GetJobs(_wfJobTable.Values, type, filters); - } - - /// - /// Loads and returns all workflow jobs based on provided filters. - /// - /// PSWorkflowJob search list - /// Represent which type of data needs to be used to apply filter. - /// Filters represents the key value pair of conditions. - /// Returns the collection of workflow instances. - internal IEnumerable GetJobs(ICollection searchList, WorkflowFilterTypes type, IDictionary filters) - { - List selectedJobs = new List(); - var filters2 = new Dictionary(filters, StringComparer.CurrentCultureIgnoreCase); - - // do a quick search on the basic v2 parameters - List narrowedList = WorkflowJobSourceAdapter.SearchJobsOnV2Parameters(searchList, filters2); - - // we have already searched based on Id, InstanceId, Name and Command - // these can now be removed from the list of items to search - string[] toRemove = { - Constants.JobMetadataSessionId, - Constants.JobMetadataInstanceId, - Constants.JobMetadataName, - Constants.JobMetadataCommand - }; - - foreach (var key in toRemove.Where(filters2.ContainsKey)) - { - filters2.Remove(key); - } - - if (filters2.Count == 0) - return narrowedList; - - foreach (var job in narrowedList) - { - // we intend to find jobs that match ALL criteria in filters - // NOT ANY criteria - bool computerNameMatch = true; - bool credentialMatch = true; - object value; - bool filterMatch = true; - - foreach (KeyValuePair filter in filters2) - { - string key = filter.Key; - if (!SearchAllFilterTypes((PSWorkflowJob)job, type, key, out value)) - { - computerNameMatch = credentialMatch = filterMatch = false; - break; - } - - // if guid is passed as a string try to convert and use the same - if (value is Guid) - { - Guid filterValueAsGuid; - LanguagePrimitives.TryConvertTo(filter.Value, CultureInfo.InvariantCulture, - out filterValueAsGuid); - if (filterValueAsGuid == (Guid)value) - continue; - filterMatch = false; - break; - } - - // PSComputerName needs to be special cased because it is actually an array. - if (key.Equals(Constants.ComputerName, StringComparison.OrdinalIgnoreCase)) - { - string[] instanceComputers = value as string[]; - if (instanceComputers == null) - { - string instanceComputer = value as string; - if (instanceComputer == null) break; - - instanceComputers = new string[] { instanceComputer }; - } - - object[] filterComputers = filter.Value as object[]; - if (filterComputers == null) - { - string computer = filter.Value as string; - if (computer == null) break; - - filterComputers = new object[] { computer }; - } - - foreach(var instanceComputer in instanceComputers) - { - computerNameMatch = false; - // for every computer we want atleast one filter - // match - foreach(var filterComputer in filterComputers) - { - string stringFilter = filterComputer as string; - if (stringFilter == null) break; - WildcardPattern filterPattern = new WildcardPattern(stringFilter, WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - - if (!filterPattern.IsMatch(instanceComputer)) continue; - computerNameMatch = true; - break; - } - if (!computerNameMatch) break; - } - if (!computerNameMatch) break; - continue; - } - - if ((key.Equals(Constants.Credential, StringComparison.OrdinalIgnoreCase))) - { - credentialMatch = false; - // PSCredential is a special case because it may be stored as a PSObject. - object credentialObject = filter.Value; - PSCredential credential = credentialObject as PSCredential; - - if (credential == null) - { - PSObject credPsObject = credentialObject as PSObject; - if (credPsObject == null) break; - - credential = credPsObject.BaseObject as PSCredential; - if (credential == null) break; - } - - Debug.Assert(credential != null); - credentialMatch = WorkflowUtils.CompareCredential(credential, value as PSCredential); - continue; - } - - if ((filter.Value is string || filter.Value is WildcardPattern) && value is string) - { - // at this point we are guaranteed that the key exists somewhere - WildcardPattern pattern; - string stringValue = filter.Value as string; - if (stringValue != null) - pattern = new WildcardPattern(stringValue, - WildcardOptions.IgnoreCase | WildcardOptions.Compiled); - else pattern = (WildcardPattern)filter.Value; - - // if the pattern is a match, matched is still true, and don't check - // to see if the selected dictionary contains the actual key value pair. - if (!pattern.IsMatch((string)value)) - { - // if the pattern is not a match, this doesn't match the given filter. - filterMatch = false; - break; - } - continue; - } - - if (value != null && value.Equals(filter.Value)) continue; - filterMatch= false; - break; - } // end of foreach - filters - - bool matched = computerNameMatch && credentialMatch && filterMatch; - - if (matched) - { - selectedJobs.Add(job); - } - } //end of outer foreach - - return selectedJobs; - } - - private static bool SearchAllFilterTypes(PSWorkflowJob job, WorkflowFilterTypes type, string key, out object value) - { - PSWorkflowContext metadata = job.PSWorkflowInstance.PSWorkflowContext; - value = null; - if (metadata == null) - return false; - var searchTable = new Dictionary> - { - {WorkflowFilterTypes.WorkflowSpecificParameters, metadata.WorkflowParameters}, - {WorkflowFilterTypes.JobMetadata, metadata.JobMetadata}, - {WorkflowFilterTypes.CommonParameters, metadata.PSWorkflowCommonParameters}, - {WorkflowFilterTypes.PrivateMetadata, metadata.PrivateMetadata} - }; - foreach(var filter in searchTable.Keys) - { - object searchResult; - if (!type.HasFlag(filter) || !SearchOneFilterType(searchTable[filter], key, out searchResult)) continue; - value = searchResult; - return true; - } - - return false; - - } - private static bool SearchOneFilterType(IDictionary tableToSearch, string key, out object value) - { - value = null; - if (tableToSearch == null) - return false; - - if (tableToSearch.ContainsKey(key)) - { - tableToSearch.TryGetValue(key, out value); - return true; - } - - return false; - } - - #endregion Searching Workflows - - #region WorkflowInstanceTable - - /// - /// The dictionary of workflow instances. - /// - private readonly ConcurrentDictionary _wfJobTable = - new ConcurrentDictionary(); - - /// - /// Add a job to the manager. - /// - /// - private void AddJob(PSWorkflowJob job) - { - Debug.Assert(job != null, "Null job passed"); - if (_wfJobTable.ContainsKey(job.InstanceId)) - { - ArgumentException exception = new ArgumentException(Resources.DuplicateInstanceId); - Tracer.TraceException(exception); - throw exception; - } - - _wfJobTable.TryAdd(job.InstanceId, job); - } - - /// - /// Retrieves job from the manager. - /// - /// The job instance Id. - /// The workflow instance Id. - /// Returns the job. - private PSWorkflowJob Get(Guid? jobInstanceId, Guid? workflowInstanceId) - { - AssertNotDisposed(); - - PSWorkflowJob jobInstance = null; - - if (jobInstanceId.HasValue) - { - _wfJobTable.TryGetValue(jobInstanceId.Value, out jobInstance); - } - - if (workflowInstanceId.HasValue) - { - jobInstance = _wfJobTable.Values.SingleOrDefault(job => job.PSWorkflowInstance.Id == workflowInstanceId.Value); - } - - if (jobInstance == null) - { - return null; - } - - return jobInstance; - } - - internal void CleanUpWorkflowJobTable() - { - List wfJobs = new List(_wfJobTable.Values); - foreach (var job in wfJobs) - { - if (job.PSWorkflowInstance.PSWorkflowContext.JobMetadata != null && - job.PSWorkflowInstance.PSWorkflowContext.JobMetadata.Count != 0) continue; - // If the job metadata is not found, this instance is not recoverable. Don't bother checking it - // and remove it from the table. - PSWorkflowJob removedJob; - _wfJobTable.TryRemove(job.InstanceId, out removedJob); - if (removedJob != null) - removedJob.Dispose(); - } - } - - internal void ClearWorkflowManagerInstanceTable() - { - foreach(var job in _wfJobTable.Values) - { - job.Dispose(); - } - _wfJobTable.Clear(); - } - - internal void RemoveChildJob(Job2 childWorkflowJob) - { - if (WorkflowJobSourceAdapter.GetInstance().GetJobManager() == this) - { - WorkflowJobSourceAdapter.GetInstance().RemoveChildJob(childWorkflowJob); - } - else - { - this.RemoveJob(childWorkflowJob.InstanceId); - } - } - - - /// - /// Remove the workflow with provided job instance id. - /// - /// The job instance id. - public void RemoveJob(Guid instanceId) - { - AssertNotDisposed(); - - Tracer.WriteMessage(Facility + "Removing job instance with id: " + instanceId); - PSWorkflowJob job; - _wfJobTable.TryGetValue(instanceId, out job); - if (job == null) return; - - lock (lockObjects.GetLockObject(instanceId)) - { - _wfJobTable.TryGetValue(instanceId, out job); - if (job == null) return; - - try - { - if (!job.IsFinishedState(job.JobStateInfo.State)) - { - try - { - job.StopJob(true, "Remove"); - } - catch (ObjectDisposedException) - { - Tracer.WriteMessage(Facility, "RemoveJob", job.PSWorkflowInstance.Id, "Workflow Job is already disposed. so removing it."); - } - } - } - finally - { - job.PSWorkflowInstance.RemoveInstance(); - job.Dispose(); - _wfJobTable.TryRemove(instanceId, out job); - - lockObjects.RemoveLockObject(instanceId); - } - } - - StructuredTracer.WorkflowDeletedFromDisk(instanceId, string.Empty); - StructuredTracer.WorkflowCleanupPerformed(instanceId); - } - - /// - /// Unload/Forget the workflow with provided job instance id. - /// This method is used to dispose unloaded idle workflows. - /// - /// The job instance id. - public void UnloadJob(Guid instanceId) - { - AssertNotDisposed(); - - lock (lockObjects.GetLockObject(instanceId)) - { - Tracer.WriteMessage(Facility + "Forgetting job instance with id: " + instanceId); - - PSWorkflowJob job = GetJob(instanceId); - - if (job == null) - return; - - job.Dispose(); // igorse: why do we call Dispose before removing from _wfJobTable? - - _wfJobTable.TryRemove(job.InstanceId, out job); - } - } - - private void DoUnloadJob(object state) - { - Collection col = state as Collection; - Debug.Assert(col != null, "The state object should be of type collection"); - - Debug.Assert(col[0].GetType() == typeof(Guid), "The first element in the collection should be of type Guid"); - Debug.Assert(col[1].GetType() == typeof(ManualResetEvent), "The second element in the collection should be of type ManualResetEvent"); - - Guid instanceId = (Guid)col[0]; - ManualResetEvent handle = (ManualResetEvent)col[1]; - - UnloadJob(instanceId); - handle.Set(); - } - - /// - /// UnloadAllJobs - /// - public void UnloadAllJobs() - { - AssertNotDisposed(); - - List waitHandles = new List(); - - foreach (KeyValuePair job in _wfJobTable) - { - ManualResetEvent handle = new ManualResetEvent(false); - ThreadPool.QueueUserWorkItem(DoUnloadJob, new Collection { job.Key, handle }); - waitHandles.Add(handle); - } - - if (waitHandles.Count > 0) - { - if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) - { - foreach (WaitHandle handle in waitHandles) - handle.WaitOne(); - } - else - { - WaitHandle.WaitAll(waitHandles.ToArray()); - } - - // now disposing all wait handles - foreach (WaitHandle handle in waitHandles) - handle.Dispose(); - } - } - - #endregion WorkflowInstanceTable - - } - - /// - /// Provides list of workflow filter types. - /// - [Flags] - internal enum WorkflowFilterTypes - { - /// - /// Empty flags - not used, would indicate to search no collections. - /// - None = 0, - - /// - /// Filters will be applicable to job metadata collection. - /// - JobMetadata = 1, - - /// - /// Filters will be applicable to caller private metadata collection. - /// - PrivateMetadata = 2, - - /// - /// Filters will be applicable to workflow specific parameters defined by the workflow author. - /// - WorkflowSpecificParameters = 4, - - /// - /// Filters will be applicable to common parameters on all workflows. - /// - CommonParameters = 8, - - /// - /// Use all filters. - /// - All = JobMetadata | PrivateMetadata | WorkflowSpecificParameters | CommonParameters - } - - internal class LockObjectsCollection - { - private object syncLock = new object(); - private Dictionary lockObjects = new Dictionary(); - - internal object GetLockObject(Guid id) - { - lock (syncLock) - { - if(lockObjects.ContainsKey(id) == false) - { - lockObjects.Add(id, new object()); - } - - return lockObjects[id]; - } - } - - internal void RemoveLockObject(Guid id) - { - if (lockObjects.ContainsKey(id) == false) - return; - - lock (syncLock) - { - if (lockObjects.ContainsKey(id) == false) - return; - - lockObjects.Remove(id); - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowMetadatas.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowMetadatas.cs deleted file mode 100644 index d23cdda830a..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowMetadatas.cs +++ /dev/null @@ -1,105 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System.Collections.Generic; - - /// - /// Define all the metadatas. - /// - public class PSWorkflowContext - { - /// - /// The parameters, which need to be passed to the workflow engine. - /// - private Dictionary _workflowParameters; - - /// - /// The ubiquitous parameters, which are also passed to the engine. - /// - private Dictionary _psWorkflowCommonParameters; - - /// - /// The metadata, which contains all the information related to job and client like, job-id, connection-id and application-id etc. - /// - private Dictionary jobMetadata; - - /// - /// The metadata, which is specific to the caller and doesn't contain any information related to workflow execution. - /// - private Dictionary privateMetadata; - - /// - /// Default Constructor. - /// - public PSWorkflowContext() - { - this._workflowParameters = null; - this._psWorkflowCommonParameters = null; - this.jobMetadata = null; - this.privateMetadata = null; - } - - /// - /// Workflow metadatas constructor. - /// - /// The parameters, which need to be passed to the workflow engine. - /// The ubiquitous parameters, which are also passed to the engine. - /// The metadata, which contains all the information related to job and client like, job-id, connection-id and application-id etc. - /// The metadata, which is specific to the caller and doesn't contain any information related to workflow execution. - public PSWorkflowContext( - Dictionary workflowParameters, - Dictionary workflowCommonParameters, - Dictionary jobMetadata, - Dictionary privateMetadata) - { - this._workflowParameters = workflowParameters; - this._psWorkflowCommonParameters = workflowCommonParameters; - this.jobMetadata = jobMetadata; - this.privateMetadata = privateMetadata; - } - - /// - /// Gets the input to workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary WorkflowParameters - { - get { return this._workflowParameters; } - set { this._workflowParameters = value; } - } - - /// - /// Gets the input to workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary PSWorkflowCommonParameters - { - get { return this._psWorkflowCommonParameters; } - set { this._psWorkflowCommonParameters = value; } - } - - /// - /// Gets the input to workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary JobMetadata - { - get { return this.jobMetadata; } - set { this.jobMetadata = value; } - } - - /// - /// Gets the input to workflow. - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public Dictionary PrivateMetadata - { - get { return this.privateMetadata; } - set { this.privateMetadata = value; } - } - - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowPerformanceCounterSetInfo.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowPerformanceCounterSetInfo.cs deleted file mode 100644 index 883a22f6e78..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowPerformanceCounterSetInfo.cs +++ /dev/null @@ -1,107 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Diagnostics.PerformanceData; -using System.Management.Automation; -using System.Management.Automation.PerformanceData; - -namespace Microsoft.PowerShell.Workflow -{ - /// - /// PSWorkflowPerformanceCounterSetInfo class contains - /// the data essential to create an - /// instance of PSCounterSetRegistrar for monitoring - /// workflow performance - /// - internal static class PSWorkflowPerformanceCounterSetInfo - { - internal static Guid ProviderId = new Guid("{5db760bc-64b2-4da7-b4ef-7dab105fbb8c}"); - /// - /// If some other assemblies (e.g. Microsoft.PowerShell.Workflow.Activities) need - /// access to the counters, then they would need to specify the CounterSetId, - /// alongwith the counterId. Hence, CounterSetId is public. - /// - internal static Guid CounterSetId = new Guid("{faa17411-9025-4b86-8b5e-ce2f32b06e13}"); - internal static CounterSetInstanceType CounterSetType = CounterSetInstanceType.Multiple; - internal static CounterInfo[] CounterInfoArray = - new CounterInfo[]{ - new CounterInfo(PSWorkflowPerformanceCounterIds.FailedWorkflowJobsCount,CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.FailedWorkflowJobsPerSec,CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ResumedWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.RunningWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.RunningWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.StoppedWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.SucceededWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.SucceededWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.SuspendedWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.SuspendedWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.TerminatedWorkflowJobsPerSec, CounterType.RateOfCountPerSecond64), - //new CounterInfo(PSWorkflowPerformanceCounterIds.TerminatedExceptionWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.WaitingWorkflowJobsCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrBusyProcessesCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrFailedRequestsQueueLength, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrIncomingRequestsPerSec, CounterType.RateOfCountPerSecond64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrPendingRequestsQueueLength, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrCreatedProcessesCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrDisposedProcessesCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.ActivityHostMgrProcessesPoolSize, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingPendingRequestsQueueLength, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingRequestsBeingServicedCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingForcedToWaitRequestsQueueLength, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingConnectionsCreatedCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingConnectionsDisposedCount, CounterType.RawData64), - new CounterInfo(PSWorkflowPerformanceCounterIds.PSRemotingConnectionsClosedReopenedCount, CounterType.RawData64) - }; - } - - /// - /// PSWorkflowPerformanceCounterIds enumerates the - /// list of valid performance counter ids related to Powershell Workflow. - /// NOTE: The prime reason for making this not an enum are as follows: - /// (1) Everytime the enum will have to be typecasted to an int before invoking any - /// of the Performance Counters API. - /// (2) In case of M3P, its possible that some of the perf counters might be updated - /// by ActivityBase assembly, in which such perf counter ids might need to have - /// public access modifier, instead of internal. - /// - internal static class PSWorkflowPerformanceCounterIds - { - internal const int FailedWorkflowJobsCount = 1; - internal const int FailedWorkflowJobsPerSec = FailedWorkflowJobsCount + 1; - internal const int ResumedWorkflowJobsCount = FailedWorkflowJobsPerSec + 1; - internal const int ResumedWorkflowJobsPerSec = ResumedWorkflowJobsCount + 1; - internal const int RunningWorkflowJobsCount = ResumedWorkflowJobsPerSec + 1; - internal const int RunningWorkflowJobsPerSec = RunningWorkflowJobsCount + 1; - internal const int StoppedWorkflowJobsCount = RunningWorkflowJobsPerSec + 1; - internal const int StoppedWorkflowJobsPerSec = StoppedWorkflowJobsCount + 1; - internal const int SucceededWorkflowJobsCount = StoppedWorkflowJobsPerSec + 1; - internal const int SucceededWorkflowJobsPerSec = SucceededWorkflowJobsCount + 1; - internal const int SuspendedWorkflowJobsCount = SucceededWorkflowJobsPerSec + 1; - internal const int SuspendedWorkflowJobsPerSec = SuspendedWorkflowJobsCount + 1; - internal const int TerminatedWorkflowJobsCount = SuspendedWorkflowJobsPerSec + 1; - internal const int TerminatedWorkflowJobsPerSec = TerminatedWorkflowJobsCount + 1; - internal const int WaitingWorkflowJobsCount = TerminatedWorkflowJobsPerSec + 1; - internal const int ActivityHostMgrBusyProcessesCount = WaitingWorkflowJobsCount + 1; - internal const int ActivityHostMgrFailedRequestsPerSec = ActivityHostMgrBusyProcessesCount + 1; - internal const int ActivityHostMgrFailedRequestsQueueLength = ActivityHostMgrFailedRequestsPerSec + 1; - internal const int ActivityHostMgrIncomingRequestsPerSec = ActivityHostMgrFailedRequestsQueueLength + 1; - internal const int ActivityHostMgrPendingRequestsQueueLength = ActivityHostMgrIncomingRequestsPerSec + 1; - internal const int ActivityHostMgrCreatedProcessesCount = ActivityHostMgrPendingRequestsQueueLength + 1; - internal const int ActivityHostMgrDisposedProcessesCount = ActivityHostMgrCreatedProcessesCount + 1; - internal const int ActivityHostMgrProcessesPoolSize = ActivityHostMgrDisposedProcessesCount + 1; - internal const int PSRemotingPendingRequestsQueueLength = ActivityHostMgrProcessesPoolSize + 1; - internal const int PSRemotingRequestsBeingServicedCount = PSRemotingPendingRequestsQueueLength + 1; - internal const int PSRemotingForcedToWaitRequestsQueueLength = PSRemotingRequestsBeingServicedCount + 1; - internal const int PSRemotingConnectionsCreatedCount = PSRemotingForcedToWaitRequestsQueueLength + 1; - internal const int PSRemotingConnectionsDisposedCount = PSRemotingConnectionsCreatedCount + 1; - internal const int PSRemotingConnectionsClosedReopenedCount = PSRemotingConnectionsDisposedCount + 1; - }; - - -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowRuntimeCompilation.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowRuntimeCompilation.cs deleted file mode 100644 index 0955fa66e65..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowRuntimeCompilation.cs +++ /dev/null @@ -1,265 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Globalization; - using System.Diagnostics.CodeAnalysis; - using System.Management.Automation.Tracing; - -#if _NOTARMBUILD_ - using Microsoft.Build.Evaluation; - using Microsoft.Build.Logging; -#endif - using System.Activities.XamlIntegration; - using System.IO; - using System.Xaml; - using System.Xml; - using System.Collections.Generic; - using System.Runtime.InteropServices; - - /// - /// This class is responsible for compiling dependent workflows. - /// - internal class WorkflowRuntimeCompilation - { - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - - /// - /// The template project which is used for default project values. - /// - private const string Template_Project = @"ReleaseAnyCPU10.02.0LibraryPropertiesv4.5512truefullfalsebin\Debug\DEBUG;TRACEprompt4pdbonlytruebin\Release\TRACEprompt4"; - - /// - /// The runtime generated project name. - /// - internal string ProjectName { get; set; } - - /// - /// The runtime generated Project folder path. - /// - internal string ProjectFolderPath { get; set; } - - /// - /// The runtime generated Project file path (.csprj file). - /// - internal string ProjectFilePath { get; set; } - - /// - /// The runtime generated build log path. - /// - internal string BuildLogPath { get; set; } - - /// - /// The runtime generated assembly name. - /// - internal string AssemblyName { get; set; } - - /// - /// The runtime generated assembly path. - /// - internal string AssemblyPath { get; set; } - - /// - /// The returned code of project.Build. - /// - internal bool BuildReturnedCode { get; set; } - - private string _projectRoot; - /// - /// Default constructor. - /// - internal WorkflowRuntimeCompilation() - { - if (IsRunningOnProcessorArchitectureARM()) - { - Tracer.WriteMessage("The workflow Calling workflow is not supported so throwing the exception."); - throw new NotSupportedException(Resources.WFCallingWFNotSupported); - } - - this.ProjectName = "Workflow_" + Guid.NewGuid().ToString("N"); - this._projectRoot = Path.Combine(Path.GetTempPath(), @"PSWorkflowCompilation\"+this.ProjectName); - this.ProjectFolderPath = Path.Combine(this._projectRoot, "Project"); - this.ProjectFilePath = Path.Combine(this.ProjectFolderPath, "RuntimeProject.csproj"); - this.BuildLogPath = Path.Combine(this.ProjectFolderPath, "Build.Log"); - this.AssemblyName = this.ProjectName; - this.AssemblyPath = Path.Combine(this._projectRoot, this.ProjectName + ".dll"); - } - - /// - /// Compiling the workflow xamls into the assembly. - /// - internal void Compile(List dependentWorkflows, Dictionary requiredAssemblies) - { - if (IsRunningOnProcessorArchitectureARM()) - { - Tracer.WriteMessage("The workflow Calling workflow is not supported so throwing the exception."); - throw new NotSupportedException(Resources.WFCallingWFNotSupported); - } - -// Note that the _NOT_ARMBUILD_ flag is not a global build flag and needs to be set in the corresponding sources.inc file as appropriate. -#if _NOTARMBUILD_ - DirectoryInfo folder = new DirectoryInfo(this.ProjectFolderPath); - folder.Create(); - - List workflowFiles = new List(); - try - { - // Dump the files - foreach (string dependentWorkflow in dependentWorkflows) - { - string newFileName = Path.Combine(this.ProjectFolderPath, Path.GetRandomFileName() + ".xaml"); - File.WriteAllText(newFileName, dependentWorkflow); - workflowFiles.Add(newFileName); - } - - File.WriteAllText(this.ProjectFilePath, Template_Project); - } - catch (Exception e) - { - Tracer.TraceException(e); - throw; - } - - using (ProjectCollection projects = new ProjectCollection()) - { - Project project = projects.LoadProject(this.ProjectFilePath); - - project.SetProperty("AssemblyName", this.AssemblyName); - - HashSet Assemblies = new HashSet(); - - foreach (string file in workflowFiles) - { - project.AddItem("XamlAppDef", file); - - XamlXmlReader reader = new XamlXmlReader(XmlReader.Create(file, new XmlReaderSettings { XmlResolver = null }), new XamlSchemaContext()); - using (reader) - { - while (reader.Read()) - { - if (reader.NodeType == XamlNodeType.NamespaceDeclaration) - { - string _namespace = reader.Namespace.Namespace.ToLowerInvariant(); - - if (_namespace.IndexOf("assembly=", StringComparison.OrdinalIgnoreCase) > -1) - { - List filters = new List(); - filters.Add("assembly="); - string[] results = _namespace.Split(filters.ToArray(), StringSplitOptions.RemoveEmptyEntries); - if (results.Length > 1 && !string.IsNullOrEmpty(results[1])) - { - string requiredAssemblyLocation; - if (requiredAssemblies != null && requiredAssemblies.Count > 0 && requiredAssemblies.TryGetValue(results[1], out requiredAssemblyLocation)) - { - Assemblies.Add(requiredAssemblyLocation); - } - else - { - Assemblies.Add(results[1]); - } - } - } - } - } - } - } - - foreach (string assembly in Assemblies) - { - project.AddItem("Reference", assembly); - } - - project.Save(this.ProjectFilePath); - - FileLogger logger = new FileLogger(); - logger.Parameters = "logfile=" + this.BuildLogPath; - - this.BuildReturnedCode = false; - - // According to MSDN, http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.aspx - // multiple project collections can exist within an app domain. However, these must not build concurrently. - // Therefore, we need a static lock to prevent multiple threads from accessing this call. - lock (_syncObject) - { - this.BuildReturnedCode = project.Build(logger); - } - - logger.Shutdown(); - - // If compilation succeeded, delete the project files. - // - if (this.BuildReturnedCode) - { - string generatedAssemblyPath = Path.Combine(this.ProjectFolderPath, @"obj\Release\" + this.ProjectName + ".dll"); - if (File.Exists(generatedAssemblyPath)) - { - File.Move(generatedAssemblyPath, this.AssemblyPath); - } - - try - { - System.IO.Directory.Delete(this.ProjectFolderPath, true); - } - catch (Exception e) - { - Tracer.TraceException(e); - // Ignoring the exceptions from Delete of temp directory. - } - } - } -#endif - } - - static object _syncObject = new object(); - - /// - /// Return true/false to indicate whether the processor architecture is ARM - /// - /// - internal static bool IsRunningOnProcessorArchitectureARM() - { - // Important: - // this function has a clone in SMA in admin\monad\src\utils\PsUtils.cs - // if you are making any changes specific to this function then update the clone as well. - - NativeMethods.SYSTEM_INFO sysInfo = new NativeMethods.SYSTEM_INFO(); - NativeMethods.GetSystemInfo(ref sysInfo); - return sysInfo.wProcessorArchitecture == NativeMethods.PROCESSOR_ARCHITECTURE_ARM; - } - - private static class NativeMethods - { - // Important: - // this clone has a clone in SMA in admin\monad\src\utils\PsUtils.cs - // if you are making any changes specific to this class then update the clone as well. - - internal const ushort PROCESSOR_ARCHITECTURE_INTEL = 0; - internal const ushort PROCESSOR_ARCHITECTURE_ARM = 5; - internal const ushort PROCESSOR_ARCHITECTURE_IA64 = 6; - internal const ushort PROCESSOR_ARCHITECTURE_AMD64 = 9; - internal const ushort PROCESSOR_ARCHITECTURE_UNKNOWN = 0xFFFF; - - [StructLayout(LayoutKind.Sequential)] - internal struct SYSTEM_INFO - { - public ushort wProcessorArchitecture; - public ushort wReserved; - public uint dwPageSize; - public IntPtr lpMinimumApplicationAddress; - public IntPtr lpMaximumApplicationAddress; - public UIntPtr dwActiveProcessorMask; - public uint dwNumberOfProcessors; - public uint dwProcessorType; - public uint dwAllocationGranularity; - public ushort wProcessorLevel; - public ushort wProcessorRevision; - }; - - [DllImport("kernel32.dll")] - internal static extern void GetSystemInfo(ref SYSTEM_INFO lpSystemInfo); - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowStatusEventArgs.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowStatusEventArgs.cs deleted file mode 100644 index 6b0cff1c081..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowStatusEventArgs.cs +++ /dev/null @@ -1,126 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Diagnostics.CodeAnalysis; - - /// - /// Possible states of workflow instance. - /// - internal enum WorkflowInstanceState - { - /// - /// Not initialized with any workflow yet. - /// - NotStarted = 0, - - /// - /// Workflow execution is loaded but not yet started. - /// - Loaded = 1, - - /// - /// Workflow is currently executing. - /// - Executing = 2, - - /// - /// Workflow execution is completed. - /// - CompletedAndClosed = 3, - - /// - /// Faulted state, once the workflow execution is completed. - /// - Faulted = 4, - - /// - /// Canceled state, once the workflow execution is completed. - /// - Canceled = 5, - - /// - /// Aborted state, once the workflow execution is completed. - /// - Aborted = 6, - - /// - /// Un-handled exception and termination state, once the workflow execution is completed. - /// - UnhandledExceptionAndTermination = 7, - - /// - /// Workflow is currently unloaded. - /// - Unloaded = 8, - - /// - /// Workflow is currently unknown. - /// - Unknown = 9, - }; - - /// - /// Provides an event definition for the status of a workflow. - /// - internal class WorkflowStatusEventArgs : EventArgs - { - /// - /// The instance id. - /// - private Guid id; - - /// - /// The workflow instance state. - /// - private WorkflowInstanceState state; - - /// - /// The workflow unhandled exception. - /// - private Exception unhandledException; - - /// - /// Constructor with instance id and state. - /// - /// The workflow Id. - /// The state of workflow - /// The unhandled exception, occurs when streams are closed. - internal WorkflowStatusEventArgs(Guid id, WorkflowInstanceState state, Exception unhandledException) - { - this.id = id; - this.state = state; - this.unhandledException = unhandledException; - } - - /// - /// Gets instance id. - /// - internal Guid Id - { - get { return this.id; } - } - - /// - /// Gets instance state. - /// - internal WorkflowInstanceState State - { - get { return this.state; } - } - - /// - /// Gets unhandled exception. - /// - internal Exception UnhandledException - { - get { return this.unhandledException; } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTimers.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTimers.cs deleted file mode 100644 index 5f42292944b..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTimers.cs +++ /dev/null @@ -1,484 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Diagnostics; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Management.Automation; - using System.Activities; - using System.Timers; - using System.Management.Automation.Tracing; - using System.Collections; - using System.Runtime.Serialization; - - /// - /// Workflow timer types. - /// - internal enum WorkflowTimerType - { - RunningTimer = 1, - - ElapsedTimer = 2, - } - - internal delegate void WorkflowTimerElapsedHandler(PSTimer sender, ElapsedEventArgs e); - - internal sealed class PSTimer : IDisposable - { - private bool disposed; - - private Timer timer; - private readonly object syncLock = new object(); - - internal WorkflowTimerType TimerType { get; private set; } - private bool IsRecurring; - private bool IsOneTimeTimer; - private TimeSpan Interval; - - private TimeSpan RemainingTime; - private DateTime? StartedAtForFirstTime; - private DateTime? StartedTime; - private bool IsRunning; - internal bool TimerReachedAlready = false; - - private event WorkflowTimerElapsedHandler Handler; - - internal PSTimer(WorkflowTimerType type, bool isRecurring, bool isOneTimeTimer, TimeSpan interval, WorkflowTimerElapsedHandler handler) - { - Debug.Assert(!(isRecurring == true && isOneTimeTimer == true), "Timer cannot be recurring and one-time-timer at the same time."); - - this.TimerType = type; - this.IsRecurring = isRecurring; - this.IsOneTimeTimer = isOneTimeTimer; - - this.Interval = interval; - this.RemainingTime = interval; - this.StartedAtForFirstTime = null; - this.StartedTime = null; - this.IsRunning = false; - - this.Handler = handler; - } - - internal PSTimer(Dictionary data, WorkflowTimerElapsedHandler handler) - { - this.TimerType = (WorkflowTimerType)data["TimerType"]; - this.IsRecurring = (bool)data["IsRecurring"]; - this.IsOneTimeTimer = (bool)data["IsOneTimeTimer"]; - this.Interval = (TimeSpan)data["Interval"]; - - if (IsRecurring == false && IsOneTimeTimer == false) - { - this.RemainingTime = (TimeSpan)data["RemainingTime"]; - } - else if (IsRecurring == false && IsOneTimeTimer == true) - { - DateTime tmpStartedAtForFirstTime = (DateTime)data["StartedAtForFirstTime"]; - TimeSpan diff = Interval - DateTime.UtcNow.Subtract(tmpStartedAtForFirstTime); - if (diff <= TimeSpan.FromSeconds(0)) - { - this.TimerReachedAlready = true; - this.RemainingTime = TimeSpan.FromSeconds(2); - } - else if (diff < TimeSpan.FromSeconds(2)) - this.RemainingTime = TimeSpan.FromSeconds(2); - else - this.RemainingTime = diff; - } - else - { - this.RemainingTime = Interval; - } - - this.StartedAtForFirstTime = null; - this.StartedTime = null; - this.IsRunning = false; - - this.Handler = handler; - - } - - internal Dictionary GetSerializedData() - { - Dictionary data = new Dictionary(); - - data.Add("TimerType", TimerType); - data.Add("IsRecurring", IsRecurring); - data.Add("IsOneTimeTimer", IsOneTimeTimer); - data.Add("Interval", Interval); - - if (IsRecurring == false && IsOneTimeTimer == false) - { - if (IsRunning) - { - Debug.Assert(StartedTime.HasValue, "Started time should have value."); - TimeSpan tmpRemainingTime = RemainingTime - DateTime.UtcNow.Subtract((DateTime)StartedTime); - if (tmpRemainingTime < TimeSpan.FromMilliseconds(0)) - tmpRemainingTime = TimeSpan.FromMilliseconds(0); - - data.Add("RemainingTime", tmpRemainingTime); - } - else - { - data.Add("RemainingTime", RemainingTime); - } - } - else if (IsRecurring == false && IsOneTimeTimer == true) - { - // one time timer - if (StartedAtForFirstTime.HasValue) - data.Add("StartedAtForFirstTime", StartedAtForFirstTime); - else - data.Add("StartedAtForFirstTime", DateTime.UtcNow); - } - - return data; - } - - private void timer_Elapsed(object sender, ElapsedEventArgs e) - { - if (disposed) - return; - - if (!IsRunning) - return; - - lock (syncLock) - { - if (!IsRunning) - return; - - if (Handler != null) - Handler(this, e); - - if (IsRecurring == false) - IsRunning = false; - } - } - - internal void Start() - { - if (disposed) - return; - - if (IsRunning) - return; - - if (Interval <= TimeSpan.FromMilliseconds(0)) - return; - - lock (syncLock) - { - if (IsRunning) - return; - - if (timer == null) - { - timer = new System.Timers.Timer(this.RemainingTime.TotalMilliseconds); - timer.AutoReset = IsRecurring; - timer.Elapsed += new ElapsedEventHandler(timer_Elapsed); - StartedAtForFirstTime = DateTime.UtcNow; - } - else - { - timer.Interval = this.RemainingTime.TotalMilliseconds; - } - - timer.Start(); - IsRunning = true; - StartedTime = DateTime.UtcNow; - } - } - - internal void Stop() - { - if (disposed) - return; - - if (!IsRunning) - return; - - lock (syncLock) - { - if (!IsRunning) - return; - - if (timer == null) - return; - - timer.Stop(); - IsRunning = false; - - Debug.Assert(StartedTime.HasValue, "Started time should have value."); - if (this.IsRecurring == false) - { - RemainingTime -= DateTime.UtcNow.Subtract((DateTime)StartedTime); - if (RemainingTime < TimeSpan.FromMilliseconds(0)) - RemainingTime = TimeSpan.FromMilliseconds(0); - } - } - } - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - private void Dispose(bool disposing) - { - if (this.disposed || !disposing) - return; - - lock (syncLock) - { - if (this.disposed) - return; - - Stop(); - - if (timer != null) - { - timer.Elapsed -= this.timer_Elapsed; - this.Handler = null; - this.timer.Dispose(); - } - - this.disposed = true; - } - } - } - - /// - /// Define all the workflow related timers. - /// - public sealed class PSWorkflowTimer : IDisposable - { - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer StructuredTracer = new Tracer(); - private readonly PSWorkflowInstance _instance; - - private bool disposed; - private readonly object syncLock = new object(); - - private Dictionary _timers; - - /// - /// Default Constructor - /// - internal PSWorkflowTimer(PSWorkflowInstance instance) - { - _instance = instance; - _timers = new Dictionary(); - } - - /// - /// Creates a workflow timer for a workflow instance based on a BLOB - /// - /// - /// - public PSWorkflowTimer(PSWorkflowInstance instance, object deserializedTimers) - { - _instance = instance; - _timers = new Dictionary(); - - if (deserializedTimers == null) throw new ArgumentNullException("deserializedTimers"); - - List deserializedTimerList = (List)deserializedTimers; - foreach (object data in deserializedTimerList) - { - Debug.Assert(data != null, "Timer data should not have been null."); - if (data != null) - { - Debug.Assert(data is Dictionary, "The timer data should be of type Dictionary."); - if (data is Dictionary) - { - PSTimer timer = new PSTimer((Dictionary)data, Timer_WorkflowTimerElapsed); - _timers.Add(timer.TimerType, timer); - } - } - } - } - - /// - /// Retrieves timers as a BLOB - /// - /// - public object GetSerializedData() - { - if (disposed) - return null; - - List serializedTimers = new List(); - foreach (PSTimer timer in _timers.Values) - { - serializedTimers.Add(timer.GetSerializedData()); - } - - return serializedTimers; - } - - internal void SetupTimer(WorkflowTimerType timerType, TimeSpan interval) - { - if (disposed) - return; - - if (_timers.ContainsKey(timerType)) - return; - - if (timerType == WorkflowTimerType.ElapsedTimer) - { - _timers.Add(timerType, new PSTimer(timerType, false, true, interval, Timer_WorkflowTimerElapsed)); - } - else if (timerType == WorkflowTimerType.RunningTimer) - { - _timers.Add(timerType, new PSTimer(timerType, false, false, interval, Timer_WorkflowTimerElapsed)); - } - } - - internal bool CheckIfTimerHasReachedAlready(WorkflowTimerType timerType) - { - if (disposed) - return false; - - if (_timers.ContainsKey(timerType) && _timers[WorkflowTimerType.ElapsedTimer].TimerReachedAlready) - return true; - - return false; - } - - internal void StartTimer(WorkflowTimerType timerType) - { - if (disposed) - return; - - if (_timers.ContainsKey(timerType)) - { - _timers[timerType].Start(); - } - } - - internal void StopTimer(WorkflowTimerType timerType) - { - if (disposed) - return; - - if (_timers.ContainsKey(timerType)) - { - _timers[timerType].Stop(); - } - } - - private void Timer_WorkflowTimerElapsed(PSTimer sender, ElapsedEventArgs e) - { - if (disposed) - return; - - StructuredTracer.Correlate(); - Tracer.WriteMessage("PSWorkflowTimer Elapsed: " + sender.TimerType); - - if (disposed) - return; - - switch (sender.TimerType) - { - case WorkflowTimerType.RunningTimer: - sender.Stop(); - TerminateWorkflow(Resources.RunningTimeReached); - break; - case WorkflowTimerType.ElapsedTimer: - sender.Stop(); - TerminateAndRemoveWorkflow(Resources.ElapsedTimeReached); - break; - } - } - - private readonly object syncElapsedLock = new object(); - private bool _elapsedTimerCalled = false; - // Terminate workflow - private void TerminateWorkflow(string reason) - { - if (disposed) return; - if (_elapsedTimerCalled) return; - - lock (syncElapsedLock) - { - - if (disposed) return; - if (_elapsedTimerCalled) return; - - try - { - Debug.Assert(_instance.PSWorkflowJob != null, "PSWorkflowJob should be set before calling terminate workflow"); - _instance.PSWorkflowJob.StopJob(true, reason); - } - catch (Exception e) - { - // logging the exception in background thread - Tracer.TraceException(e); - } - } - } - - // Terminate and Remove workflow - private void TerminateAndRemoveWorkflow(string reason) - { - _elapsedTimerCalled = true; - if (disposed) return; - - lock (syncElapsedLock) - { - if (disposed) return; - try - { - _instance.PSWorkflowJob.StopJob(true, Resources.ElapsedTimeReached); - } - catch (Exception e) - { - // logging the exception in background thread - Tracer.TraceException(e); - } - } - } - - /// - /// Dispose implementation. - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Protected virtual implementation of Dispose. - /// - /// - private void Dispose(bool disposing) - { - if (this.disposed || !disposing) - return; - - lock (syncLock) - { - if (this.disposed) - return; - - foreach (PSTimer wt in _timers.Values) - { - wt.Dispose(); - } - _timers.Clear(); - - this.disposed = true; - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTrackingParticipant.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTrackingParticipant.cs deleted file mode 100644 index a6bb12efdf3..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowTrackingParticipant.cs +++ /dev/null @@ -1,204 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Activities; - using System.Activities.Statements; - using System.Activities.Validation; - using System.Collections.Generic; - using System.Globalization; - using System.Diagnostics.CodeAnalysis; - using System.Management.Automation.Tracing; - using Microsoft.PowerShell.Activities; - using System.Activities.Tracking; - using System.Text; - - /// - /// Contains members that allow the addition of custom extension to - /// the PowerShell workflow engine. - /// - public static class PSWorkflowExtensions - { - /// - /// The custom workflow extensions delegate to use in this engine - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", - "CA1006:DoNotNestGenericTypesInMemberSignatures", - Justification = "This is needed for providing custom powershell extensions dynamically.")] - static public Func> CustomHandler { get; set; } - } - - /// - /// Implementing the workflow tracking participant, this will established with communication between activities and hosting - /// engine to perform additional task like persistence and logging etc. - /// - internal class PSWorkflowTrackingParticipant : TrackingParticipant - { - private readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private readonly Tracer _structuredTracer = new Tracer(); - - private const String participantName = "WorkflowTrackingParticipant"; - - private PSWorkflowDebugger _debugger; - private const string debugBreakActivity = "PowerShellValue"; - - /// - /// Default constructor. - /// - internal PSWorkflowTrackingParticipant() - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "{0} Created", participantName)); - } - - /// - /// Constructor. - /// - /// PSWorkflowDebugger - internal PSWorkflowTrackingParticipant(PSWorkflowDebugger debugger) - { - _debugger = debugger; - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "{0} Created", participantName)); - } - - /// - /// Retrieve each type of tracking record and perform the corresponding functionality. - /// - /// Represents the tracking record. - /// Time out for the tracking to be completed. - protected override void Track(TrackingRecord record, TimeSpan timeout) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "{0} Emitted trackRecord: {1} Level: {2}, RecordNumber: {3}", participantName, record.GetType().FullName, record.Level, record.RecordNumber)); - - WorkflowInstanceRecord workflowInstanceRecord = record as WorkflowInstanceRecord; - if (workflowInstanceRecord != null) - { - if (_structuredTracer.IsEnabled) - { - if (string.Equals(WorkflowInstanceStates.Persisted, workflowInstanceRecord.State, StringComparison.OrdinalIgnoreCase)) - { - _structuredTracer.WorkflowPersisted(workflowInstanceRecord.InstanceId); - } - else if (string.Equals(WorkflowInstanceStates.UnhandledException, workflowInstanceRecord.State, StringComparison.OrdinalIgnoreCase)) - { - WorkflowInstanceUnhandledExceptionRecord unhandledRecord = workflowInstanceRecord as WorkflowInstanceUnhandledExceptionRecord; - if (unhandledRecord != null) - { - _structuredTracer.WorkflowActivityExecutionFailed(unhandledRecord.InstanceId, - unhandledRecord.FaultSource != null ? unhandledRecord.FaultSource.Name : unhandledRecord.ActivityDefinitionId, - System.Management.Automation.Tracing.Tracer.GetExceptionString(unhandledRecord.UnhandledException)); - } - } - } - this.ProcessWorkflowInstanceRecord(workflowInstanceRecord); - } - - ActivityStateRecord activityStateRecord = record as ActivityStateRecord; - if (activityStateRecord != null) - { - if (_structuredTracer.IsEnabled) - { - ActivityInstanceState activityState = ActivityInstanceState.Executing; - if (!string.IsNullOrEmpty(activityStateRecord.State) - && Enum.TryParse(activityStateRecord.State, out activityState)) - { - if (activityState == ActivityInstanceState.Executing) - { - _structuredTracer.ActivityExecutionQueued(activityStateRecord.InstanceId, activityStateRecord.Activity.Name); - } - } - } - this.ProcessActivityStateRecord(activityStateRecord); - } - - CustomTrackingRecord customTrackingRecord = record as CustomTrackingRecord; - - if ((customTrackingRecord != null) && (customTrackingRecord.Data.Count > 0)) - { - this.ProcessCustomTrackingRecord(customTrackingRecord); - } - } - - /// - /// Process the workflow instance record. - /// - /// Record representing workflow instance record. - private void ProcessWorkflowInstanceRecord(WorkflowInstanceRecord record) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, " Workflow InstanceID: {0} Workflow instance state: {1}", record.InstanceId, record.State)); - } - - /// - /// process the activity state record. - /// - /// Record representing activity state record. - private void ProcessActivityStateRecord(ActivityStateRecord record) - { - IDictionary variables = record.Variables; - StringBuilder vars = new StringBuilder(); - - if (variables.Count > 0) - { - vars.AppendLine("\n\tVariables:"); - foreach (KeyValuePair variable in variables) - { - vars.AppendLine(String.Format(CultureInfo.InvariantCulture, "\t\tName: {0} Value: {1}", variable.Key, variable.Value)); - } - } - - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, " :Activity DisplayName: {0} :ActivityInstanceState: {1} {2}", record.Activity.Name, record.State, ((variables.Count > 0) ? vars.ToString() : String.Empty))); - } - - /// - /// Process the custom tracking record. This record will contain the persistence detail. - /// - /// Record representing custom tracking record. - private void ProcessCustomTrackingRecord(CustomTrackingRecord record) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, "\n\tUser Data:")); - - if (_debugger != null && - record.Data != null) - { - // Get breakpoint info if available. - object value; - string bp = null; - if (record.Data.TryGetValue("DebugSequencePoint", out value)) - { - bp = value as string; - record.Data.Remove("DebugSequencePoint"); - } - - // Update debugger variables. - _debugger.UpdateVariables(record.Data); - - // Pass breakpoint info to debugger, which will optionally stop WF activity execution here. - if (!string.IsNullOrEmpty(bp)) - { - try - { - string[] symbols = bp.Trim('\'', '\"').Split(':'); - ActivityPosition debuggerBP = new ActivityPosition( - symbols[2], // WF Name - Convert.ToInt32(symbols[0], CultureInfo.InvariantCulture), // Line number - Convert.ToInt32(symbols[1], CultureInfo.InvariantCulture)); // Col number - - // Debugger blocking call if breakpoint hit or debugger stepping is active. - _debugger.DebuggerCheck(debuggerBP); - } - catch (FormatException) - { } - catch (OverflowException) - { } - } - } - - foreach (string data in record.Data.Keys) - { - Tracer.WriteMessage(String.Format(CultureInfo.InvariantCulture, " \t\t {0} : {1}", data, record.Data[data])); - } - } - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowUtils.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowUtils.cs deleted file mode 100644 index 7711d021415..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowUtils.cs +++ /dev/null @@ -1,323 +0,0 @@ -using System; -using System.Security; -using System.Runtime.InteropServices; -using System.Management.Automation; -using System.Diagnostics; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Remoting; -using System.Management.Automation.Tracing; - -namespace Microsoft.PowerShell.Workflow -{ - static class WorkflowUtils - { - /// - /// CompareConnectionUri compares two connection URIs - /// by doing a comparison of elements. - /// - /// Connection info 1 - /// Connection info 2 - /// True if they match else false. - internal static bool CompareConnectionUri(WSManConnectionInfo connectionInfo1, WSManConnectionInfo connectionInfo2) - { - Debug.Assert(connectionInfo1 != null && connectionInfo2 != null, "Connections should be != null"); - Debug.Assert(!string.IsNullOrEmpty(connectionInfo1.Scheme) && !string.IsNullOrEmpty(connectionInfo1.ComputerName) - && !string.IsNullOrEmpty(connectionInfo1.AppName), "Connection URI elements should be != null or empty"); - Debug.Assert(!string.IsNullOrEmpty(connectionInfo2.Scheme) && !string.IsNullOrEmpty(connectionInfo2.ComputerName) - && !string.IsNullOrEmpty(connectionInfo2.AppName), "Connection URI elements should be != null or empty"); - - if (String.Compare(connectionInfo2.Scheme, connectionInfo1.Scheme, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - if (String.Compare(connectionInfo2.ComputerName, connectionInfo1.ComputerName, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - if (String.Compare(connectionInfo2.AppName, connectionInfo1.AppName, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - if (connectionInfo2.Port != connectionInfo1.Port) - { - return false; - } - - return true; - } // CompareConnectionUri ... - - /// - /// CompareShellUri compares two shell URIs - /// by doing a string of elements. - /// - /// Shell Uri 1 - /// Shell Uri 2 - /// True if they match else false. - internal static bool CompareShellUri(String shellUri1, String shellUri2) - { - Debug.Assert(!string.IsNullOrEmpty(shellUri1) && !string.IsNullOrEmpty(shellUri2), "Shell Uris should be != null or empty"); - - if (String.Compare(shellUri1, shellUri2, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - - return true; - } // CompareShellUri ... - - - /// - /// CompareAuthentication compares two authentication mechanisms. - /// - /// Authentication mechanism 1 - /// Authentication mechanism 2 - /// True if they match else false. - internal static bool CompareAuthentication(AuthenticationMechanism authentication1, AuthenticationMechanism authentication2) - { - return authentication1 == authentication2; - } // CompareAuthentication ... - - /// - /// CompareCredentials compares two PSCredential credentials - /// by doing a username and password comparison . - /// - /// Credential 1 - /// Credential 2 - /// True if they match else false. - internal static bool CompareCredential(PSCredential credential1, PSCredential credential2) - { - if (credential1 == null && credential2 == null) - { - return true; - } - - // check credentials if present - if (credential1 == null ^ credential2 == null) - { - return false; - } - - Debug.Assert(credential1 != null && credential2 != null - && credential1.UserName != null && credential2.UserName != null, "Credentials should be != null"); - - // check the username - if (string.Compare(credential1.UserName, credential2.UserName, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - - // check the password - if (!WorkflowUtils.ComparePassword(credential1.Password, credential2.Password)) - { - return false; - } - - return true; - } - - /// - /// ComparePassword uses native functions to perform a string match on two SecureString passwords - /// by doing a strict byte level comparison is done on the two strings. - /// The use of ReadByte allows the function to execute without marking the assembly as unsafe. - /// - /// Password 1 - /// Password 2 - /// True if they match else false. - internal static bool ComparePassword(SecureString secureString1, SecureString secureString2) - { - if (secureString1 == null && secureString2 == null) - { - return true; - } - - if (secureString1 == null ^ secureString2 == null) - { - return false; - } - - Debug.Assert(secureString1 != null && secureString2 != null, "SecureStrings should be != null"); - - if (secureString1.Length != secureString2.Length) - { - return false; - } - - IntPtr bstr1 = IntPtr.Zero; - IntPtr bstr2 = IntPtr.Zero; - - try - { - bstr1 = Marshal.SecureStringToBSTR(secureString1); - bstr2 = Marshal.SecureStringToBSTR(secureString2); - - int offset = 0; - Byte leftHigh, leftLow, rightHigh, rightLow; - bool notDone = true; - - do - { - leftLow = Marshal.ReadByte(bstr1, offset + 1); - rightLow = Marshal.ReadByte(bstr2, offset + 1); - leftHigh = Marshal.ReadByte(bstr1, offset); - rightHigh = Marshal.ReadByte(bstr2, offset); - offset += 2; - if (leftLow != rightLow || leftHigh != rightHigh) - { - return false; - } - notDone = leftLow != 0 || leftHigh != 0; // terminator - 2 null characters (0x00)? - leftLow = rightLow = leftHigh = rightHigh = 0; - } while (notDone); - - return true; - } - catch (Exception e) - { - using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - // SecureStringToBSTR or ReadByte threw exceptions - - tracer.WriteMessage("Getting an exception while comparing credentials..."); - tracer.TraceException(e); - - return false; - } - } - finally - { - if (IntPtr.Zero != bstr1) - { - Marshal.ZeroFreeBSTR(bstr1); - } - if (IntPtr.Zero != bstr2) - { - Marshal.ZeroFreeBSTR(bstr2); - } - } - } // ComparePassword ... - - /// - /// CompareCertificateThumbprint compares two certificate thumbprints - /// by doing a string comparison. - /// - /// Certificate Thumbprint 1 - /// Certificate Thumbprint 2 - /// True if they match else false. - internal static bool CompareCertificateThumbprint(String certificateThumbprint1, String certificateThumbprint2) - { - if (certificateThumbprint1 == null && certificateThumbprint2 == null) - { - return true; - } - - if (certificateThumbprint1 == null ^ certificateThumbprint2 == null) - { - return false; - } - - Debug.Assert(certificateThumbprint1 != null && certificateThumbprint2 != null, "Certificate thumbprints should be != null"); - - if (string.Compare(certificateThumbprint1, certificateThumbprint2, StringComparison.OrdinalIgnoreCase) != 0) - { - return false; - } - - return true; - } // CompareCertificateThumbprint ... - - /// - /// CompareProxySettings compares the proxy settings for two wsman connections - /// by doing a comparison of elements. - /// - /// Connection info 1 - /// Connection info 2 - /// True if they match else false. - internal static bool CompareProxySettings(WSManConnectionInfo connectionInfo1, WSManConnectionInfo connectionInfo2) - { - Debug.Assert(connectionInfo1 != null && connectionInfo2 != null, "Connections should be != null"); - - if (connectionInfo1.ProxyAccessType != connectionInfo2.ProxyAccessType) - { - return false; - } - - if (connectionInfo1.ProxyAccessType == ProxyAccessType.None) - { - return true; //stop here if no proxy access type - } - - if (connectionInfo1.ProxyAuthentication != connectionInfo2.ProxyAuthentication) - { - return false; - } - - // check the proxy credentials password - if (!WorkflowUtils.CompareCredential(connectionInfo1.ProxyCredential, connectionInfo2.ProxyCredential)) - { - return false; - } - - return true; - } // CompareProxySettings ... - - /// - /// CompareOtherWSManSettings compares the rest of the wsman settings for two wsman connections - /// by doing a comparison of elements. - /// - /// Connection info 1 - /// Connection info 2 - /// True if they match else false. - internal static bool CompareOtherWSManSettings(WSManConnectionInfo connectionInfo1, WSManConnectionInfo connectionInfo2) - { - Debug.Assert(connectionInfo1 != null && connectionInfo2 != null, "Connections should be != null"); - - if (connectionInfo1.SkipCACheck != connectionInfo2.SkipCACheck) - { - return false; - } - if (connectionInfo1.SkipCNCheck != connectionInfo2.SkipCNCheck) - { - return false; - } - if (connectionInfo1.SkipRevocationCheck != connectionInfo2.SkipRevocationCheck) - { - return false; - } - if (connectionInfo1.UseCompression != connectionInfo2.UseCompression) - { - return false; - } - if (connectionInfo1.UseUTF16 != connectionInfo2.UseUTF16) - { - return false; - } - if (connectionInfo1.MaximumConnectionRedirectionCount != connectionInfo2.MaximumConnectionRedirectionCount) - { - return false; - } - if (connectionInfo1.MaximumReceivedDataSizePerCommand != connectionInfo2.MaximumReceivedDataSizePerCommand) - { - return false; - } - if (connectionInfo1.MaximumReceivedObjectSize != connectionInfo2.MaximumReceivedObjectSize) - { - return false; - } - if (connectionInfo1.NoEncryption != connectionInfo2.NoEncryption) - { - return false; - } - if (connectionInfo1.NoMachineProfile != connectionInfo2.NoMachineProfile) - { - return false; - } - if (connectionInfo1.OutputBufferingMode != connectionInfo2.OutputBufferingMode) - { - return false; - } - - return true; - } // CompareOtherWSManSettings ... - - } // WorkflowUtils ... -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowValidation.cs b/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowValidation.cs deleted file mode 100644 index d782c7324ab..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/ServiceCore/WorkflowCore/WorkflowValidation.cs +++ /dev/null @@ -1,401 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Diagnostics; - -namespace Microsoft.PowerShell.Workflow -{ - using System; - using System.Activities; - using System.Activities.Statements; - using System.Activities.Validation; - using System.Collections.Generic; - using System.Globalization; - using System.Diagnostics.CodeAnalysis; - using System.Management.Automation.Tracing; - using Microsoft.PowerShell.Activities; - using System.Management.Automation; - using System.Collections.Concurrent; - using System.Reflection; - - /// - /// Contains members that allow for controlling the PowerShell workflow - /// engine validation mechanism. - /// - [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")] - public static class Validation - { - /// - /// The custom validator delegate to use in this engine - /// - static public Func CustomHandler { get; set; } - } - - internal class PSWorkflowValidationResults - { - internal PSWorkflowValidationResults() - { - this.IsWorkflowSuspendable = false; - this.Results = null; - } - - internal bool IsWorkflowSuspendable { get; set; } - internal ValidationResults Results { get; set; } - } - - /// - /// Validate all the activities in the workflow to check if they are allowed or not. - /// - public class PSWorkflowValidator - { - - #region Privates - - private static readonly string Facility = "WorkflowValidation : "; - private static readonly PowerShellTraceSource Tracer = PowerShellTraceSourceFactory.GetTraceSource(); - private static readonly Tracer _structuredTracer = new Tracer(); - private readonly ConcurrentDictionary _validationCache = new ConcurrentDictionary(); - - private static readonly List MandatorySystemActivities = new List { "SuspendOnError" }; - private static readonly List AllowedSystemActivities = new List - { - "DynamicActivity", - "DoWhile", - "ForEach`1", - "If", - "Parallel", - "ParallelForEach`1", - "Sequence", - "Switch`1", - "While", - "Assign", - "Assign`1", - "Delay", - "InvokeMethod", - "TerminateWorkflow", - "WriteLine", - "Rethrow", - "Throw", - "TryCatch", - "Literal`1", - "VisualBasicValue`1", - "VisualBasicReference`1", - "LocationReferenceValue`1", - "VariableValue`1", - "VariableReference`1", - "LocationReferenceReference`1", - "LambdaValue`1", - "Flowchart", - "FlowDecision", - "FlowSwitch`1", - "AddToCollection`1", - "ExistsInCollection`1", - "RemoveFromCollection`1", - "ClearCollection`1", - }; - - private static HashSet PowerShellActivitiesAssemblies = new HashSet() { - "microsoft.powershell.activities", - "microsoft.powershell.core.activities", - "microsoft.powershell.diagnostics.activities", - "microsoft.powershell.management.activities", - "microsoft.powershell.security.activities", - "microsoft.powershell.utility.activities", - "microsoft.powershell.workflow.servicecore", - "microsoft.wsman.management.activities", - }; - - private void ValidateWorkflowInternal(Activity workflow, string runtimeAssembly, PSWorkflowValidationResults validationResults) - { - Tracer.WriteMessage(Facility + "Validating a workflow."); - _structuredTracer.WorkflowValidationStarted(Guid.Empty); - - ValidationSettings validationSettings = new ValidationSettings - { - AdditionalConstraints = - { - { typeof(Activity), new List { ValidateActivitiesConstraint(runtimeAssembly, validationResults)} } - } - }; - - try - { - validationResults.Results = ActivityValidationServices.Validate(workflow, validationSettings); - } - catch (Exception e) - { - Tracer.TraceException(e); - ValidationException exception = new ValidationException(Resources.ErrorWhileValidatingWorkflow, e); - throw exception; - } - - _structuredTracer.WorkflowValidationFinished(Guid.Empty); - - } - - private Constraint ValidateActivitiesConstraint(string runtimeAssembly, PSWorkflowValidationResults validationResults) - { - DelegateInArgument activityToValidate = new DelegateInArgument(); - DelegateInArgument validationContext = new DelegateInArgument(); - - return new Constraint - { - Body = new ActivityAction - { - Argument1 = activityToValidate, - Argument2 = validationContext, - Handler = new AssertValidation - { - IsWarning = false, - - Assertion = new InArgument( - env => ValidateActivity(activityToValidate.Get(env), runtimeAssembly, validationResults)), - - Message = new InArgument( - env => string.Format(CultureInfo.CurrentCulture, Resources.InvalidActivity, activityToValidate.Get(env).GetType().FullName)) - } - } - }; - } - - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "It is called by another private method 'ValidateActivitiesConstraint'")] - private bool ValidateActivity(Activity activity, string runtimeAssembly, PSWorkflowValidationResults validationResult) - { - if (validationResult != null && validationResult.IsWorkflowSuspendable == false) - { - validationResult.IsWorkflowSuspendable = CheckIfSuspendable(activity); - } - - bool allowed = false; - Type activityType = activity.GetType(); - - // If there is a custom validator activity, then call it. If it returns true - // then just return the activity. - if (Validation.CustomHandler != null && Validation.CustomHandler.Invoke(activity)) - { - allowed = true; - } - else if (MandatorySystemActivities.Contains(activityType.Name)) - { - allowed = true; - } - else if (string.Equals(activityType.Assembly.FullName, "System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", StringComparison.OrdinalIgnoreCase)) - { - allowed = AllowedSystemActivities.Contains(activityType.Name); - } - else if (string.Equals(runtimeAssembly, activityType.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase)) - { - // Allow the activities which belong to runtime assembly containing dependent Xamls. - allowed = true; - } - else if (Configuration.PSDefaultActivitiesAreAllowed - && PowerShellActivitiesAssemblies.Contains(activityType.Assembly.GetName().Name.ToLowerInvariant())) - { - // Allow any of the activities from our product assemblies provided that product activities were - // specified as allowed activities - allowed = true; - } - else - { - // Check if the activityId matches any activity in the allowed list - // as a full name or as pattern. - foreach (string allowedActivity in Configuration.AllowedActivity ?? new string[0]) - { - // skipping * because it has already been checked above - if (string.Equals(allowedActivity, PSWorkflowConfigurationProvider.PSDefaultActivities, StringComparison.OrdinalIgnoreCase)) - continue; - - if (activity is System.Activities.Activity) - { - if (false - || IsMatched(allowedActivity, activityType.Name) - || IsMatched(allowedActivity, activityType.FullName) - || IsMatched(allowedActivity, activityType.Assembly.GetName().Name + "\\" + activityType.Name) - || IsMatched(allowedActivity, activityType.Assembly.GetName().Name + "\\" + activityType.FullName) - || IsMatched(allowedActivity, activityType.Assembly.GetName().FullName + "\\" + activityType.Name) - || IsMatched(allowedActivity, activityType.Assembly.GetName().FullName + "\\" + activityType.FullName)) - { - allowed = true; - break; - } - } - } - } - - string displayName = activity.DisplayName; - - if (string.IsNullOrEmpty(displayName)) - displayName = this.GetType().Name; - - if (allowed) - { - _structuredTracer.WorkflowActivityValidated(Guid.Empty, displayName, activityType.FullName); - } - else - { - _structuredTracer.WorkflowActivityValidationFailed(Guid.Empty, displayName, activityType.FullName); - } - - return allowed; - } - - private bool CheckIfSuspendable(Activity activity) - { - if (string.Equals(activity.GetType().ToString(), "Microsoft.PowerShell.Activities.PSPersist", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (string.Equals(activity.GetType().ToString(), "Microsoft.PowerShell.Activities.Suspend", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - PSActivity psactivity = activity as PSActivity; - if (psactivity != null && psactivity.PSPersist != null && psactivity.PSPersist.Expression != null) - { - return true; - } - - Sequence seqActivity = activity as Sequence; - if (seqActivity != null && seqActivity.Variables != null && seqActivity.Variables.Count > 0) - { - foreach (Variable var in seqActivity.Variables) - { - if (string.Equals(var.Name, WorkflowPreferenceVariables.PSPersistPreference, StringComparison.OrdinalIgnoreCase)) - return true; - } - } - - Parallel parActivity = activity as Parallel; - if (parActivity != null && parActivity.Variables != null && parActivity.Variables.Count > 0) - { - foreach (Variable var in parActivity.Variables) - { - if (string.Equals(var.Name, WorkflowPreferenceVariables.PSPersistPreference, StringComparison.OrdinalIgnoreCase)) - return true; - } - } - - return false; - } - - private static bool IsMatched(string allowedActivity, string match) - { - return (WildcardPattern.ContainsWildcardCharacters(allowedActivity) - ? new WildcardPattern(allowedActivity, WildcardOptions.IgnoreCase).IsMatch(match) - : string.Equals(allowedActivity, match, StringComparison.OrdinalIgnoreCase)); - } - - #endregion Privates - - #region Public methods - - /// - /// PSWorkflowValidator - /// - /// - public PSWorkflowValidator(PSWorkflowConfigurationProvider configuration) - { - if (configuration == null) - throw new ArgumentNullException("configuration"); - - if (TestMode) - { - System.Threading.Interlocked.Increment(ref ObjectCounter); - } - - this.Configuration = configuration; - } - - /// - /// Validate the workflow, if it is using the allowed activities. - /// - /// The workflow, which needs to be validated. - /// The additional runtime assembly, which is needed in validation process. - public ValidationResults ValidateWorkflow(Activity workflow, string runtimeAssembly) - { - PSWorkflowValidationResults validationResults = new PSWorkflowValidationResults(); - ValidateWorkflowInternal(workflow, runtimeAssembly, validationResults); - - return validationResults.Results; - } - - #endregion Public methods - - #region Internal Accessors - - internal PSWorkflowConfigurationProvider Configuration - { - get; - private set; - } - - internal PSWorkflowValidationResults ValidateWorkflow(Guid referenceId, Activity workflow, string runtimeAssembly) - { - PSWorkflowValidationResults validationResults = null; - - if (_validationCache.ContainsKey(referenceId)) - { - _validationCache.TryGetValue(referenceId, out validationResults); - } - else - { - validationResults = new PSWorkflowValidationResults(); - - this.ValidateWorkflowInternal(workflow, runtimeAssembly, validationResults); - - // sanity check to ensure cache isn't growing unbounded - if (_validationCache.Keys.Count == Configuration.ValidationCacheLimit) - { - _validationCache.Clear(); - } - _validationCache.TryAdd(referenceId, validationResults); - - } - - return validationResults; - } - - internal void ProcessValidationResults(ValidationResults results) - { - if (results == null) - throw new ArgumentNullException("results"); - - if (results.Errors != null && results.Errors.Count == 0) - return; - - string errorMessage = string.Empty; - - foreach (ValidationError error in results.Errors) - { - errorMessage += error.Message; - errorMessage += "\n"; - } - - ValidationException exception = new ValidationException(errorMessage); - - Tracer.TraceException(exception); - _structuredTracer.WorkflowValidationError(Guid.Empty); - throw exception; - } - - #endregion Internal Accessors - - # region Test Only Variables & Accessors - - // For testing purpose ONLY - internal static bool TestMode = false; - // For testing purpose ONLY - internal static long ObjectCounter = 0; - - - internal bool IsActivityAllowed(Activity activity, string runtimeAssembly) - { - return ValidateActivity(activity, runtimeAssembly, null); - } - # endregion Test Only Variables & Accessors - - } -} diff --git a/src/Microsoft.PowerShell.Workflow.ServiceCore/resources/Resources.resx b/src/Microsoft.PowerShell.Workflow.ServiceCore/resources/Resources.resx deleted file mode 100644 index 46e30c38e18..00000000000 --- a/src/Microsoft.PowerShell.Workflow.ServiceCore/resources/Resources.resx +++ /dev/null @@ -1,429 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - The workflow cannot be started because the compiled activity for the workflow was not found. Cache the workflow definition by calling DefinitionCache.Instance.Add. - DefinitionCache.Instance.Add should not be localized. It is the name of the method. - - - Could not find an activity for the command name '{0}'. - - - The async result is already finished. A finished operation cannot be called twice. - - - An End operation has already been called for this async result. - - - The IAsyncResult specified is not valid. - IAsynResult should not be localized. This is a type. - - - A duplicate instance ID was found. The instance ID already exists. - - - The OwnerId of the instance is mismatched. - - - The Version of the instance is mismatched. - - - The activity is not valid: {0} - - - The async result from the file provider is not valid. - - - Workflow files must have a file name extension of .xaml or .dll, not {0}. If you are using a workflow with a file name extension of .dll, then you must pass only one assembly. - - - The job cannot be started. The job state must be NotStarted and the job can only be started once. - - - The job could not be removed because a job matching the specified criteria could not be found. - - - A parameter element specified in the configuration does not have a Name attribute. - - - The workflow job could not be created because the JobInvocationInfo does not contain a JobDefinition. Initialize the JobInvocationInfo with a JobDefinition to create a workflow job. - JobInvocationInfo and JobDefinition should not be localized. These are types. - - - The workflow job could not be created because the JobInvocationInfo specifies a type other than WorkflowJobSourceAdapter. Correct the JobSourceAdapter type or verify that the correct type is being used. - JobInvocationInfo, WorkflowJobSourceAdapter and JobSourceAdapter should not be localized. These are types. - - - No workflows were found in the specified path '{0}'. - - - A workflow instance does not exist for the specified Instance ID. - - - Null or empty workflow is provided. - - - Parameter {0} has been specified more than once. Remove multiple references to this parameter from the session configuration, and then try again. - {0} should not be localized. It is the name of a parameter - - - The workflow job could not be created because the JobInvocationInfo was not properly initialized. Initialize the JobInvocationInfo with workflow information. - JobInvocationInfo should not be localized. This is a type - - - A parameter element specified in the configuration does not have a Value attribute. - Value should not be localized. This is an attribute. - - - This workflow is already loaded. - - - The workflow instance state file does not exist. - - - The workflow job cannot be resumed, either because persistence data could not be saved completely, or saved persistence data has been corrupted. You must restart the workflow. - - - The workflow is not running. The current state of the workflow instance is {0}. - - - The workflow instance is not yet loaded. - - - The specified IAsyncResultObject is not valid. - IAsyncResult should not be localized. This is a type. - - - The specified runspace is not valid. - - - One or more workflows that are part of the specified job could not be removed. - - - Cannot remove the PSWorkflowJob without removing its parent. Please remove the ContainerParentJob that contains this PSWorkflowJob. - PSWorkflowJob and ContainerParentJob should not be localized. These are types. - - - Shutdown request is in progress, workflow is suspending. - - - The syntax of a parallel script block is 'Parallel { <commands> }' - "Parallel" should not be localized. This is syntax. - - - Suspend could not be completed. - - - Unblock is not a supported operation on a PSWorkflowJob. - PSWorkflowJob should not be localized. This is a type. - - - Workflow complete. - - - Removing workflow instance from the table. - - - An elapsed time-out occurred, and the workflow was stopped. - - - Because a running time-out has occurred, the workflow has been stopped. - - - The workflow could not be resumed. - - - Importing workflow from file '{0}'. - - - The argument to -PSParameterCollection can only contain one entry where PSComputerName is set to '*'. Remove the extra entries and try again. - PSParameterCollection and PSComputerName should not be localized. These are parameter names. - - - The workflow '{0}' could not be started: {1} - - - The Persistence store has reached its maximum size. Free up storage space by removing completed jobs. - - - Value {0} is out of the allowed range. Allowed range is from {1} to {2}. - - - "Internal Error: The object is marked for restricted construction, but another type is constructing it elsewhere. -Target Type:{0} -Allowed Creator Type: {1} -Actual Creator Type: {2} -Method: {3} - - - This PSWorkflowJob was recovered from another session in a suspended state. This can indicate that the workflow was interrupted by a process failure, or a system restart. Run Resume-Job to try resuming the workflow. - PSWorkflowJob and Resume-Job should not be localized. These are a type and a cmdlet name. - - - An exception has occurred while validating the workflow. - - - The argument to -PSParameterCollection can not contain AsJob, JobName, InputObject or PSParameterCollection as entries. Remove the extra entries and try again. - PSParameterCollection, AsJob, JobName, InputObject should not be localized. These are the names of parameters. - - - Activity-Specific Parameters - - - Cannot supply both connection URI and computer name. - - - Connectivity - - - The activity cannot continue running because an error occurred while importing dependent module(s) '{0}' specified for activity '{1}'. To fix this problem, verify that the module exists on the computer. If the module is not required, remove references to the module from the activity. - - - The value of the PersistencePath parameter {0} exceeds the maximum allowed length. The persistence path should not be greater than {1} characters. - - - Input and Output - - - {0} line:{1} char:{2} - - - Compilation error while building dependent workflows. For details see {0}. - - - Only one computer name is allowed. - - - Suspend operation is in progress. - - - Stop operation is in progress. - - - This activity does not support custom remote management. - - - The force-suspend time-out period cannot be zero or null. Specify a time-out period that is greater than zero. - - - The PSParameterCollection parameter can only be used with AsJob, JobName and InputObject. Move the additional parameters to within the PSParameterCollection argument and try again. - PSParameterCollection, AsJob, JobName, InputObject should not be localized. These are parameter names. - - - An error occurred while trying to run the activity. - - - The activity has exceeded the specified maximum running time of {0} seconds. - - - The label is not valid: The label '{0}' doesn't exist. - - - The activity failed because the connection to the remote computer '{0}' was disconnected. - {0} is a computer name and should not be localized - - - Parameters defined for workflow functions do not support ValueFromPipeline. Remove this attribute and import or define the workflow again. - - - The workflow definition has been recycled, because the workflow definition cache limit of {0} has been reached. Import the workflow or module again, and then retry the operation. - - - A credential cannot be specified when NegotiateWithImplicitCredential is specified as the value of Authentication. - - - The PSCredential parameter is supported only when the PSComputerName or PSConnectionUri parameters are used. The PSCredential parameter has been ignored. - - - A CertificateThumbprint cannot be specified when Credential is specified. - - - The '{0}' cmdlet does not implement the InputObject property. - {0} is a cmdlet name and should not be localized - - - The workflow name "{0}" is not a valid workflow. Specify a valid workflow name and then try the command again. - {0} is the name of the workflow and should not be localized - - - Completed - - - Failed - - - Running - - - The specified ActivityImplementationContext is not of type CimActivityImplementationContext. - - - This workflow job cannot be suspended because there are no checkpoints (also called persistence points) in the workflow. To make the workflow job suspendable, add checkpoints to the workflow. - -For more information about how to add checkpoints, see the help topics for Windows PowerShell Workflow. - - - The workflow job cannot be suspended because it does not include checkpoints (also called persistence points). To make the workflow suspendable, use the PSPersist parameter of the workflow. To suspend the workflow forcibly, use the Force parameter of the Suspend-Job cmdlet. - - - The provided XAML cannot be null. - - - The bookmark '{0}' is not valid; it does not exist in the workflow engine. - - - The input objects cannot be serialized. Serialized data is required to suspend and resume workflows. To resolve the error, verify that the values of all variables and parameters are of types that can be serialized. - - - The input objects cannot be serialized. This might prevent the workflow from being resumed correctly. The serialization error might indicate a problem with the data contract that is defined at the type or attribute level. To resolve the problem, verify that the values of all variables and parameters can be serialized. Serialization error is: {0}. - - - The job has been added to the queue to be resumed. - - - The '{0}' language mode is not supported. Supported language modes are FullLanguage and ConstrainedLanguage. - - - Workflows cannot call workflows on this platform. - - - The debugger Quit command has stopped the workflow job. - - - "Cannot process the debugger command until the debugger is stopped." - - - Retrying activity action: attempt {0}. - {0} is a number and should not be localized. - - - Cannot load the workflow. Only signed in-box XAML-based workflows or script-based workflows are supported in the current language mode. - - - Retrying activity connection: attempt {0} of {1}. - {0} and {1} are numbers and should not be localized. - - - Workflow variables cannot be modified inside the debugger. The command or script did not run because it attempted to modify variable '${0}'. - - - - The debugger Quit command has stopped the workflow job. - -