3 using System
.Reflection
;
4 using System
.ComponentModel
;
5 using System
.Collections
;
6 using System
.Collections
.Generic
;
7 using System
.Collections
.Specialized
;
8 using System
.Diagnostics
;
9 using System
.Diagnostics
.CodeAnalysis
;
11 using System
.Xml
.XPath
;
12 using System
.Xml
.Schema
;
14 using System
.Runtime
.Serialization
.Formatters
.Binary
;
15 using System
.Threading
;
17 using System
.Security
.Permissions
;
18 using System
.Security
.Cryptography
;
20 using System
.Workflow
.Runtime
;
21 using System
.Workflow
.ComponentModel
;
22 using System
.Workflow
.Runtime
.Hosting
;
23 using System
.Workflow
.Runtime
.Tracking
;
25 namespace System
.Workflow
.Runtime
28 /// Creates TrackingListener instances
30 internal class TrackingListenerFactory
32 private List
<TrackingService
> _services
= null;
33 private bool _initialized
= false;
34 private Dictionary
<Guid
, WeakReference
> _listeners
= new Dictionary
<Guid
, WeakReference
>();
35 private volatile object _listenerLock
= new object();
37 private System
.Timers
.Timer _timer
= null;
38 private double _interval
= 60000;
40 private TrackingProfileManager _profileManager
= new TrackingProfileManager();
42 internal TrackingListenerFactory()
46 internal TrackingProfileManager TrackingProfileManager
48 get { return _profileManager; }
53 /// <param name="skedExec"></param>
54 internal void Initialize(WorkflowRuntime runtime
)
58 _services
= runtime
.TrackingServices
;
59 _profileManager
.Initialize(runtime
);
60 runtime
.WorkflowExecutorInitializing
+= WorkflowExecutorInitializing
;
62 _timer
= new System
.Timers
.Timer();
63 _timer
.Interval
= _interval
;
64 _timer
.AutoReset
= false; // ensure that only one timer thread at a time
65 _timer
.Elapsed
+= new ElapsedEventHandler(Cleanup
);
73 /// Clean up static state created in Initialize
75 internal void Uninitialize(WorkflowRuntime runtime
)
79 _profileManager
.Uninitialize();
80 runtime
.WorkflowExecutorInitializing
-= WorkflowExecutorInitializing
;
81 _timer
.Elapsed
-= new ElapsedEventHandler(Cleanup
);
91 /// Callback for associating tracking listeners to in memory instances. Fires for new and loading instances.
93 /// <param name="sender">WorkflowExecutor</param>
94 /// <param name="e"></param>
95 void WorkflowExecutorInitializing(object sender
, WorkflowRuntime
.WorkflowExecutorInitializingEventArgs e
)
98 throw new ArgumentNullException("sender");
101 throw new ArgumentNullException("e");
103 if (!typeof(WorkflowExecutor
).IsInstanceOfType(sender
))
104 throw new ArgumentException("sender");
106 WorkflowExecutor exec
= (WorkflowExecutor
)sender
;
108 // Add an event to clean up the WeakRef entry
109 exec
.WorkflowExecutionEvent
+= new EventHandler
<WorkflowExecutor
.WorkflowExecutionEventArgs
>(WorkflowExecutionEvent
);
110 TrackingCallingState trackingCallingState
= exec
.TrackingCallingState
;
111 TrackingListenerBroker listenerBroker
= (TrackingListenerBroker
)exec
.RootActivity
.GetValue(WorkflowExecutor
.TrackingListenerBrokerProperty
);
112 if (listenerBroker
!= null)
114 listenerBroker
.ReplaceServices(exec
.WorkflowRuntime
.TrackingServiceReplacement
);
116 TrackingListener listener
= null;
118 // Check if we still have a weakref to the listener for this instance
119 WeakReference weakref
= null;
125 found
= _listeners
.TryGetValue(exec
.InstanceId
, out weakref
);
132 // Instead of checking IsAlive take a ref to the Target
133 // so that it isn't GC'd underneath us.
134 listener
= weakref
.Target
as TrackingListener
;
136 catch (InvalidOperationException
)
139 // This seems weird but according to msdn
140 // accessing Target can throw ???
141 // Ignore because it's the same as a null target.
145 // If listener is null because we didn't find the wr in the cache
146 // or because the Target has been GC'd create a new listener
147 if (null != listener
)
149 listener
.Broker
= listenerBroker
;
153 Debug
.Assert(null != listenerBroker
, "TrackingListenerBroker should not be null during loading");
154 listener
= GetTrackingListener(exec
.WorkflowDefinition
, exec
, listenerBroker
);
155 if (null != listener
)
158 weakref
.Target
= listener
;
163 _listeners
.Add(exec
.ID
, new WeakReference(listener
));
172 // New instance is being created
173 listener
= GetTrackingListener(exec
.WorkflowDefinition
, exec
);
175 if (null != listener
)
177 exec
.RootActivity
.SetValue(WorkflowExecutor
.TrackingListenerBrokerProperty
, listener
.Broker
);
180 _listeners
.Add(exec
.ID
, new WeakReference(listener
));
184 exec
.RootActivity
.SetValue(WorkflowExecutor
.TrackingListenerBrokerProperty
, new TrackingListenerBroker());
187 if (null != listener
)
189 exec
.WorkflowExecutionEvent
+= new EventHandler
<WorkflowExecutor
.WorkflowExecutionEventArgs
>(listener
.WorkflowExecutionEvent
);
194 void WorkflowExecutionEvent(object sender
, WorkflowExecutor
.WorkflowExecutionEventArgs e
)
198 case WorkflowEventInternal
.Aborted
:
199 case WorkflowEventInternal
.Completed
:
200 case WorkflowEventInternal
.Terminated
:
202 // The instance is done - remove
203 // the WeakRef from our list
204 WorkflowExecutor exec
= (WorkflowExecutor
)sender
;
207 _listeners
.Remove(exec
.ID
);
215 void Cleanup(object sender
, ElapsedEventArgs e
)
217 List
<Guid
> _toRemove
= new List
<Guid
>();
218 if ((null != _listeners
) || (_listeners
.Count
> 0))
222 foreach (KeyValuePair
<Guid
, WeakReference
> kvp
in _listeners
)
224 if (null == kvp
.Value
.Target
)
225 _toRemove
.Add(kvp
.Key
);
227 if (_toRemove
.Count
> 0)
229 foreach (Guid g
in _toRemove
)
230 _listeners
.Remove(g
);
245 /// Return a tracking listener for a new instance
247 /// <param name="sked">SequentialWorkflow for which the tracking listener will be associated</param>
248 /// <param name="skedExec">ScheduleExecutor for the schedule instance</param>
249 /// <returns>New TrackingListener instance</returns>
250 internal TrackingListener
GetTrackingListener(Activity sked
, WorkflowExecutor skedExec
)
253 Initialize(skedExec
.WorkflowRuntime
);
255 return GetListener(sked
, skedExec
, null);
259 /// Return a tracking listener for an existing instance (normally used during loading)
261 /// <param name="sked">SequentialWorkflow for which the tracking listener will be associated</param>
262 /// <param name="skedExec">ScheduleExecutor for the schedule instance</param>
263 /// <param name="broker">TrackingListenerBroker</param>
264 /// <returns>New TrackingListener instance</returns>
265 internal TrackingListener
GetTrackingListener(Activity sked
, WorkflowExecutor skedExec
, TrackingListenerBroker broker
)
268 Initialize(skedExec
.WorkflowRuntime
);
272 WorkflowTrace
.Tracking
.TraceEvent(TraceEventType
.Error
, 0, ExecutionStringManager
.NullTrackingBroker
);
276 return GetListener(sked
, skedExec
, broker
);
279 private TrackingListener
GetListenerFromWRCache(Guid instanceId
)
281 WeakReference wr
= null;
282 TrackingListener listener
= null;
285 if (!_listeners
.TryGetValue(instanceId
, out wr
))
286 throw new InvalidOperationException(string.Format(System
.Globalization
.CultureInfo
.InvariantCulture
, ExecutionStringManager
.ListenerNotInCache
, instanceId
));
288 listener
= wr
.Target
as TrackingListener
;
290 if (null == listener
)
291 throw new ObjectDisposedException(string.Format(System
.Globalization
.CultureInfo
.InvariantCulture
, ExecutionStringManager
.ListenerNotInCacheDisposed
, instanceId
));
297 internal void ReloadProfiles(WorkflowExecutor exec
)
299 // Keep control events from other threads out
300 using (new ServiceEnvironment(exec
.RootActivity
))
302 using (exec
.ExecutorLock
.Enter())
304 // check if this is a valid in-memory instance
305 if (!exec
.IsInstanceValid
)
306 throw new InvalidOperationException(ExecutionStringManager
.WorkflowNotValid
);
308 // suspend the instance
309 bool localSuspend
= exec
.Suspend(ExecutionStringManager
.TrackingProfileUpdate
);
314 TrackingListener listener
= GetListenerFromWRCache(exec
.InstanceId
);
315 listener
.ReloadProfiles(exec
, exec
.InstanceId
);
321 // @undone: for now this will not return till the instance is done
322 // Once Kumar has fixed 4335, we can enable this.
330 internal void ReloadProfiles(WorkflowExecutor exec
, Guid instanceId
, ref TrackingListenerBroker broker
, ref List
<TrackingChannelWrapper
> channels
)
332 Type workflowType
= exec
.WorkflowDefinition
.GetType();
334 // Ask every tracking service if they want to reload
335 // even if they originally returned null for a profile
336 foreach (TrackingService service
in _services
)
338 TrackingProfile profile
= null;
339 TrackingChannelWrapper w
= null;
341 // Check if the service wants to reload a profile
342 if (service
.TryReloadProfile(workflowType
, instanceId
, out profile
))
346 for (i
= 0; i
< channels
.Count
; i
++)
348 if (service
.GetType() == channels
[i
].TrackingServiceType
)
356 // If we don't have a profile, remove what we had for this service type (if anything)
361 broker
.RemoveService(w
.TrackingServiceType
);
362 channels
.RemoveAt(i
);
367 // Parse the new profile - instance only, the cache is not involved
368 RTTrackingProfile rtp
= new RTTrackingProfile(profile
, exec
.WorkflowDefinition
, workflowType
);
369 rtp
.IsPrivate
= true;
374 // This is a new profile, create new channel, channelwrapper and broker item
375 List
<string> activityCallPath
= null;
376 Guid callerInstanceId
= Guid
.Empty
;
377 TrackingCallingState trackingCallingState
= exec
.TrackingCallingState
;
378 Debug
.Assert((null != trackingCallingState
), "WorkflowState is null");
379 IList
<string> path
= null;
380 Guid context
= GetContext(exec
.RootActivity
), callerContext
= Guid
.Empty
, callerParentContext
= Guid
.Empty
;
382 // Use CallerActivityPathProxy to determine if this is an invoked instance
383 if (trackingCallingState
!= null)
385 path
= trackingCallingState
.CallerActivityPathProxy
;
386 if ((null != path
) && (path
.Count
> 0))
388 activityCallPath
= new List
<string>(path
);
390 Debug
.Assert(Guid
.Empty
!= trackingCallingState
.CallerWorkflowInstanceId
, "Instance has an ActivityCallPath but CallerInstanceId is empty");
391 callerInstanceId
= trackingCallingState
.CallerWorkflowInstanceId
;
393 callerContext
= trackingCallingState
.CallerContextGuid
;
394 callerParentContext
= trackingCallingState
.CallerParentContextGuid
;
398 TrackingParameters tp
= new TrackingParameters(instanceId
, workflowType
, exec
.WorkflowDefinition
, activityCallPath
, callerInstanceId
, context
, callerContext
, callerParentContext
);
399 TrackingChannel channel
= service
.GetTrackingChannel(tp
);
401 TrackingChannelWrapper wrapper
= new TrackingChannelWrapper(channel
, service
.GetType(), workflowType
, rtp
);
402 channels
.Add(wrapper
);
404 Type t
= service
.GetType();
405 broker
.AddService(t
, rtp
.Version
);
406 broker
.MakeProfileInstance(t
);
411 // Don't need to call MakeProfilePrivate on the wrapper
412 // because we've already marked it as private and we already
413 // have a private copy of it.
414 //w.MakeProfilePrivate( exec );
415 w
.SetTrackingProfile(rtp
);
416 broker
.MakeProfileInstance(w
.TrackingServiceType
);
422 internal Guid
GetContext(Activity activity
)
424 return ((ActivityExecutionContextInfo
)ContextActivityUtils
.ContextActivity(activity
).GetValue(Activity
.ActivityExecutionContextInfoProperty
)).ContextGuid
;
427 private TrackingListener
GetListener(Activity sked
, WorkflowExecutor skedExec
, TrackingListenerBroker broker
)
429 if ((null == sked
) || (null == skedExec
))
431 WorkflowTrace
.Tracking
.TraceEvent(TraceEventType
.Error
, 0, ExecutionStringManager
.NullParameters
);
435 if ((null == _services
) || (_services
.Count
<= 0))
438 bool load
= (null != broker
);
440 List
<TrackingChannelWrapper
> channels
= GetChannels(sked
, skedExec
, skedExec
.InstanceId
, sked
.GetType(), ref broker
);
442 if ((null == channels
) || (0 == channels
.Count
))
445 return new TrackingListener(this, sked
, skedExec
, channels
, broker
, load
);
448 private List
<TrackingChannelWrapper
> GetChannels(Activity schedule
, WorkflowExecutor exec
, Guid instanceID
, Type workflowType
, ref TrackingListenerBroker broker
)
450 if (null == _services
)
453 bool initBroker
= false;
456 broker
= new TrackingListenerBroker();
460 List
<TrackingChannelWrapper
> channels
= new List
<TrackingChannelWrapper
>();
462 List
<string> activityCallPath
= null;
463 Guid callerInstanceId
= Guid
.Empty
;
464 Guid context
= GetContext(exec
.RootActivity
), callerContext
= Guid
.Empty
, callerParentContext
= Guid
.Empty
;
466 Debug
.Assert(exec
is WorkflowExecutor
, "Executor is not WorkflowExecutor");
467 TrackingCallingState trackingCallingState
= exec
.TrackingCallingState
;
468 TrackingListenerBroker trackingListenerBroker
= (TrackingListenerBroker
)exec
.RootActivity
.GetValue(WorkflowExecutor
.TrackingListenerBrokerProperty
);
469 IList
<string> path
= trackingCallingState
!= null ? trackingCallingState
.CallerActivityPathProxy
: null;
471 // Use CallerActivityPathProxy to determine if this is an invoked instance
472 if ((null != path
) && (path
.Count
> 0))
474 activityCallPath
= new List
<string>(path
);
476 Debug
.Assert(Guid
.Empty
!= trackingCallingState
.CallerWorkflowInstanceId
, "Instance has an ActivityCallPath but CallerInstanceId is empty");
477 callerInstanceId
= trackingCallingState
.CallerWorkflowInstanceId
;
479 callerContext
= trackingCallingState
.CallerContextGuid
;
480 callerParentContext
= trackingCallingState
.CallerParentContextGuid
;
483 TrackingParameters parameters
= new TrackingParameters(instanceID
, workflowType
, exec
.WorkflowDefinition
, activityCallPath
, callerInstanceId
, context
, callerContext
, callerParentContext
);
485 for (int i
= 0; i
< _services
.Count
; i
++)
487 TrackingChannel channel
= null;
488 Type serviceType
= _services
[i
].GetType();
491 // See if the service has a profile for this schedule type
492 // If not we don't do any tracking for the service
494 RTTrackingProfile profile
= null;
497 // If we've created the broker get the current version of the profile
500 profile
= _profileManager
.GetProfile(_services
[i
], schedule
);
505 broker
.AddService(serviceType
, profile
.Version
);
510 // Only reload the services that are in the broker
511 // If services that weren't originally associated to an instance
512 // wish to join that instance they should call ReloadTrackingProfiles
513 if (!broker
.ContainsService(serviceType
))
516 if (broker
.IsProfileInstance(serviceType
))
518 profile
= _profileManager
.GetProfile(_services
[i
], schedule
, instanceID
);
521 throw new InvalidOperationException(ExecutionStringManager
.MissingProfileForService
+ serviceType
.ToString());
523 profile
.IsPrivate
= true;
528 if (broker
.TryGetProfileVersionId(serviceType
, out versionId
))
530 profile
= _profileManager
.GetProfile(_services
[i
], schedule
, versionId
);
533 throw new InvalidOperationException(ExecutionStringManager
.MissingProfileForService
+ serviceType
.ToString() + ExecutionStringManager
.MissingProfileForVersion
+ versionId
.ToString());
535 // If the profile is marked as private clone the instance we got from the cache
536 // The cloned instance is marked as private during the cloning
537 if (broker
.IsProfilePrivate(serviceType
))
539 profile
= profile
.Clone();
540 profile
.IsPrivate
= true;
549 // If profile is not null get a channel
550 channel
= _services
[i
].GetTrackingChannel(parameters
);
553 throw new InvalidOperationException(ExecutionStringManager
.NullChannel
);
555 channels
.Add(new TrackingChannelWrapper(channel
, _services
[i
].GetType(), workflowType
, profile
));
563 /// Handles subscribing to status change events and receiving event notifications.
565 internal class TrackingListener
567 private List
<TrackingChannelWrapper
> _channels
= null;
568 private TrackingListenerBroker _broker
= null;
569 private TrackingListenerFactory _factory
= null;
571 protected TrackingListener()
575 internal TrackingListener(TrackingListenerFactory factory
, Activity sked
, WorkflowExecutor exec
, List
<TrackingChannelWrapper
> channels
, TrackingListenerBroker broker
, bool load
)
577 if ((null == sked
) || (null == broker
))
579 WorkflowTrace
.Tracking
.TraceEvent(TraceEventType
.Error
, 0, ExecutionStringManager
.NullParameters
);
583 _channels
= channels
;
585 // Keep a reference to the broker so that we can hand it out when adding subscriptions
588 // Give the broker our reference so that it can call us back on behalf of subscriptions
589 _broker
.TrackingListener
= this;
592 internal TrackingListenerBroker Broker
594 get { return _broker; }
595 set { _broker = value; }
598 internal void ReloadProfiles(WorkflowExecutor exec
, Guid instanceId
)
601 // Ask the factory to redo the channels and broker
602 _factory
.ReloadProfiles(exec
, instanceId
, ref _broker
, ref _channels
);
606 #region Event Handlers
608 internal void ActivityStatusChange(object sender
, WorkflowExecutor
.ActivityStatusChangeEventArgs e
)
610 WorkflowTrace
.Tracking
.TraceInformation("TrackingListener::ActivityStatusChange - Received Activity Status Change Event for activity {0}", e
.Activity
.QualifiedName
);
613 throw new ArgumentNullException("sender");
615 if (!typeof(WorkflowExecutor
).IsInstanceOfType(sender
))
616 throw new ArgumentException("sender");
619 throw new ArgumentNullException("e");
621 WorkflowExecutor exec
= (WorkflowExecutor
)sender
;
623 if ((null == _channels
) || (_channels
.Count
<= 0))
625 WorkflowTrace
.Tracking
.TraceEvent(TraceEventType
.Error
, 0, ExecutionStringManager
.NoChannels
);
629 Activity activity
= e
.Activity
;
631 if (!SubscriptionRequired(activity
, exec
))
634 // Get the shared data that is the same for each tracking channel that gets a record
635 Guid parentContextGuid
= Guid
.Empty
, contextGuid
= Guid
.Empty
;
636 GetContext(activity
, exec
, out contextGuid
, out parentContextGuid
);
638 DateTime dt
= DateTime
.UtcNow
;
639 int eventOrderId
= _broker
.GetNextEventOrderId();
641 foreach (TrackingChannelWrapper wrapper
in _channels
)
644 // Create a record for each tracking channel
645 // Each channel gets a distinct record because extract data will almost always be different.
646 ActivityTrackingRecord record
= new ActivityTrackingRecord(activity
.GetType(), activity
.QualifiedName
, contextGuid
, parentContextGuid
, activity
.ExecutionStatus
, dt
, eventOrderId
, null);
648 bool extracted
= wrapper
.GetTrackingProfile(exec
).TryTrackActivityEvent(activity
, activity
.ExecutionStatus
, exec
, record
);
650 // Only send the record to the channel if the profile indicates that it is interested
651 // This doesn't mean that the Body will always have data in it,
652 // it may be an empty extraction (just header info)
654 wrapper
.TrackingChannel
.Send(record
);
658 internal void UserTrackPoint(object sender
, WorkflowExecutor
.UserTrackPointEventArgs e
)
660 if (!typeof(WorkflowExecutor
).IsInstanceOfType(sender
))
661 throw new ArgumentException("sender is not WorkflowExecutor");
663 WorkflowExecutor exec
= (WorkflowExecutor
)sender
;
664 Activity activity
= e
.Activity
;
666 DateTime dt
= DateTime
.UtcNow
;
667 int eventOrderId
= _broker
.GetNextEventOrderId();
669 Guid parentContextGuid
, contextGuid
;
670 GetContext(activity
, exec
, out contextGuid
, out parentContextGuid
);
672 foreach (TrackingChannelWrapper wrapper
in _channels
)
674 UserTrackingRecord record
= new UserTrackingRecord(activity
.GetType(), activity
.QualifiedName
, contextGuid
, parentContextGuid
, dt
, eventOrderId
, e
.Key
, e
.Args
);
676 if (wrapper
.GetTrackingProfile(exec
).TryTrackUserEvent(activity
, e
.Key
, e
.Args
, exec
, record
))
677 wrapper
.TrackingChannel
.Send(record
);
681 internal void WorkflowExecutionEvent(object sender
, WorkflowExecutor
.WorkflowExecutionEventArgs e
)
684 throw new ArgumentNullException("sender");
686 WorkflowExecutor exec
= sender
as WorkflowExecutor
;
688 throw new ArgumentException(ExecutionStringManager
.InvalidSenderWorkflowExecutor
);
690 // Many events are mapped "forward" and sent to tracking services
691 // (Persisting->Persisted, SchedulerEmpty->Idle)
692 // This is so that a batch is always available when a tracking service gets an event.
693 // Without this tracking data could be inconsistent with the state of the instance.
696 case WorkflowEventInternal
.Creating
:
697 NotifyChannels(TrackingWorkflowEvent
.Created
, e
, exec
);
699 case WorkflowEventInternal
.Starting
:
700 NotifyChannels(TrackingWorkflowEvent
.Started
, e
, exec
);
702 case WorkflowEventInternal
.Suspending
:
703 NotifyChannels(TrackingWorkflowEvent
.Suspended
, e
, exec
);
705 case WorkflowEventInternal
.Resuming
:
706 NotifyChannels(TrackingWorkflowEvent
.Resumed
, e
, exec
);
708 case WorkflowEventInternal
.Persisting
:
709 NotifyChannels(TrackingWorkflowEvent
.Persisted
, e
, exec
);
711 case WorkflowEventInternal
.Unloading
:
712 NotifyChannels(TrackingWorkflowEvent
.Unloaded
, e
, exec
);
714 case WorkflowEventInternal
.Loading
:
715 NotifyChannels(TrackingWorkflowEvent
.Loaded
, e
, exec
);
717 case WorkflowEventInternal
.Completing
:
718 NotifyChannels(TrackingWorkflowEvent
.Completed
, e
, exec
);
719 NotifyChannelsOfCompletionOrTermination();
721 case WorkflowEventInternal
.Aborting
:
722 NotifyChannels(TrackingWorkflowEvent
.Aborted
, e
, exec
);
724 case WorkflowEventInternal
.Terminating
:
725 NotifyChannels(TrackingWorkflowEvent
.Terminated
, e
, exec
);
726 NotifyChannelsOfCompletionOrTermination();
728 case WorkflowEventInternal
.Exception
:
729 NotifyChannels(TrackingWorkflowEvent
.Exception
, e
, exec
);
731 case WorkflowEventInternal
.SchedulerEmpty
:
732 NotifyChannels(TrackingWorkflowEvent
.Idle
, e
, exec
);
734 case WorkflowEventInternal
.UserTrackPoint
:
735 UserTrackPoint(exec
, (WorkflowExecutor
.UserTrackPointEventArgs
)e
);
737 case WorkflowEventInternal
.ActivityStatusChange
:
738 ActivityStatusChange(exec
, (WorkflowExecutor
.ActivityStatusChangeEventArgs
)e
);
740 case WorkflowEventInternal
.DynamicChangeBegin
:
741 DynamicUpdateBegin(exec
, (WorkflowExecutor
.DynamicUpdateEventArgs
)e
);
743 case WorkflowEventInternal
.DynamicChangeRollback
:
744 DynamicUpdateRollback(exec
, (WorkflowExecutor
.DynamicUpdateEventArgs
)e
);
746 case WorkflowEventInternal
.DynamicChangeCommit
:
747 DynamicUpdateCommit(exec
, (WorkflowExecutor
.DynamicUpdateEventArgs
)e
);
754 internal void DynamicUpdateBegin(object sender
, WorkflowExecutor
.DynamicUpdateEventArgs e
)
757 throw new ArgumentNullException("sender");
759 if (!typeof(WorkflowExecutor
).IsInstanceOfType(sender
))
760 throw new ArgumentException("sender");
762 WorkflowExecutor exec
= (WorkflowExecutor
)sender
;
764 // WorkflowChangeEventArgs may be null or the WorkflowChanges may be null or empty
765 // If so there's no work to do here
766 if (null == e
.ChangeActions
)
769 // Clone the profiles to create instance specific copies (if they aren't already)
770 MakeProfilesPrivate(exec
);
772 // Give the profiles the changes. At this point we are in a volatile state.
773 // Profiles must act as if the changes will succeed but roll back any internal changes if they do not.
774 foreach (TrackingChannelWrapper channel
in _channels
)
776 channel
.GetTrackingProfile(exec
).WorkflowChangeBegin(e
.ChangeActions
);
780 internal void DynamicUpdateRollback(object sender
, WorkflowExecutor
.DynamicUpdateEventArgs e
)
783 throw new ArgumentNullException("sender");
785 if (!typeof(WorkflowExecutor
).IsInstanceOfType(sender
))
786 throw new ArgumentException("sender");
788 WorkflowExecutor exec
= (WorkflowExecutor
)sender
;
790 foreach (TrackingChannelWrapper channel
in _channels
)
792 channel
.GetTrackingProfile(exec
).WorkflowChangeRollback();
796 internal void DynamicUpdateCommit(object sender
, WorkflowExecutor
.DynamicUpdateEventArgs e
)
799 throw new ArgumentNullException("sender");
801 if (!typeof(WorkflowExecutor
).IsInstanceOfType(sender
))
802 throw new ArgumentException("sender");
804 WorkflowExecutor exec
= (WorkflowExecutor
)sender
;
806 DateTime dt
= DateTime
.UtcNow
;
807 foreach (TrackingChannelWrapper channel
in _channels
)
809 channel
.GetTrackingProfile(exec
).WorkflowChangeCommit();
812 // Notify tracking channels of changes
813 int eventOrderId
= _broker
.GetNextEventOrderId();
815 foreach (TrackingChannelWrapper wrapper
in _channels
)
817 WorkflowTrackingRecord rec
= new WorkflowTrackingRecord(TrackingWorkflowEvent
.Changed
, dt
, eventOrderId
, new TrackingWorkflowChangedEventArgs(e
.ChangeActions
, exec
.WorkflowDefinition
));
818 if (wrapper
.GetTrackingProfile(exec
).TryTrackInstanceEvent(TrackingWorkflowEvent
.Changed
, rec
))
819 wrapper
.TrackingChannel
.Send(rec
);
823 #endregion Event Handlers
825 #region Private Methods
827 private void NotifyChannels(TrackingWorkflowEvent evt
, WorkflowExecutor
.WorkflowExecutionEventArgs e
, WorkflowExecutor exec
)
829 DateTime dt
= DateTime
.UtcNow
;
830 int eventOrderId
= _broker
.GetNextEventOrderId();
832 foreach (TrackingChannelWrapper wrapper
in _channels
)
834 EventArgs args
= null;
837 case TrackingWorkflowEvent
.Suspended
:
838 args
= new TrackingWorkflowSuspendedEventArgs(((WorkflowExecutor
.WorkflowExecutionSuspendingEventArgs
)e
).Error
);
840 case TrackingWorkflowEvent
.Terminated
:
841 WorkflowExecutor
.WorkflowExecutionTerminatingEventArgs wtea
= (WorkflowExecutor
.WorkflowExecutionTerminatingEventArgs
)e
;
842 if (null != wtea
.Exception
)
843 args
= new TrackingWorkflowTerminatedEventArgs(wtea
.Exception
);
845 args
= new TrackingWorkflowTerminatedEventArgs(wtea
.Error
);
847 case TrackingWorkflowEvent
.Exception
:
848 WorkflowExecutor
.WorkflowExecutionExceptionEventArgs weea
= (WorkflowExecutor
.WorkflowExecutionExceptionEventArgs
)e
;
849 args
= new TrackingWorkflowExceptionEventArgs(weea
.Exception
, weea
.CurrentPath
, weea
.OriginalPath
, weea
.ContextGuid
, weea
.ParentContextGuid
);
852 WorkflowTrackingRecord rec
= new WorkflowTrackingRecord(evt
, dt
, eventOrderId
, args
);
853 if (wrapper
.GetTrackingProfile(exec
).TryTrackInstanceEvent(evt
, rec
))
854 wrapper
.TrackingChannel
.Send(rec
);
858 private void NotifyChannelsOfCompletionOrTermination()
860 foreach (TrackingChannelWrapper wrapper
in _channels
)
861 wrapper
.TrackingChannel
.InstanceCompletedOrTerminated();
864 private void GetContext(Activity activity
, WorkflowExecutor exec
, out Guid contextGuid
, out Guid parentContextGuid
)
866 contextGuid
= _factory
.GetContext(activity
);
868 if (null != activity
.Parent
)
869 parentContextGuid
= _factory
.GetContext(activity
.Parent
);
871 parentContextGuid
= contextGuid
;
873 Debug
.Assert(contextGuid
!= Guid
.Empty
, "TrackingContext is empty");
874 Debug
.Assert(parentContextGuid
!= Guid
.Empty
, "Parent TrackingContext is empty");
878 /// Clone all profiles to create private versions in order to hold subscriptions for dynamic changes
880 private void MakeProfilesPrivate(WorkflowExecutor exec
)
882 foreach (TrackingChannelWrapper channel
in _channels
)
884 channel
.MakeProfilePrivate(exec
);
885 _broker
.MakeProfilePrivate(channel
.TrackingServiceType
);
889 /// Determine if subscriptions are needed
891 /// <param name="activity">Activity for which to check subscription needs</param>
892 /// <returns></returns>
893 private bool SubscriptionRequired(Activity activity
, WorkflowExecutor exec
)
896 // Give each channel a chance to prep itself
899 foreach (TrackingChannelWrapper channel
in _channels
)
901 if ((channel
.GetTrackingProfile(exec
).ActivitySubscriptionNeeded(activity
)) && (!needed
))
912 /// This is a lightweight class that is serialized so that the TrackingListener doesn't have to be.
913 /// Every subscription that the listener adds holds a reference to this class.
914 /// When an instance is loaded the broker is given to the listener factory and the listener factory
915 /// gives the broker the new listener. This saves us from having to persist the listener itself which
916 /// means that while we do need to persist a list of service types and their profile version we don't
917 /// have to persist the channels themselves (and we can't control how heavy channels get as they are host defined).
920 internal class TrackingListenerBroker
: System
.Runtime
.Serialization
.ISerializable
923 private TrackingListener _listener
= null;
924 private int _eventOrderId
= 0;
925 private Dictionary
<Guid
, ServiceProfileContainer
> _services
= new Dictionary
<Guid
, ServiceProfileContainer
>();
927 internal TrackingListenerBroker()
931 internal TrackingListenerBroker(TrackingListener listener
)
933 _listener
= listener
;
936 internal TrackingListener TrackingListener
939 // FxCops minbar complains because this isn't used.
940 // The Setter is required; seems weird not to have a getter.
941 //get { return _listener; }
942 set { _listener = value; }
945 internal bool ContainsService(Type trackingServiceType
)
947 return _services
.ContainsKey(HashHelper
.HashServiceType(trackingServiceType
));
950 internal void AddService(Type trackingServiceType
, Version profileVersionId
)
952 _services
.Add(HashHelper
.HashServiceType(trackingServiceType
), new ServiceProfileContainer(profileVersionId
));
955 internal void ReplaceServices(Dictionary
<string, Type
> replacements
)
957 if (replacements
!= null && replacements
.Count
> 0)
959 ServiceProfileContainer item
;
960 foreach (KeyValuePair
<string, Type
> replacement
in replacements
)
962 Guid previous
= HashHelper
.HashServiceType(replacement
.Key
);
963 if (_services
.TryGetValue(previous
, out item
))
965 _services
.Remove(previous
);
966 Guid current
= HashHelper
.HashServiceType(replacement
.Value
);
967 if (!_services
.ContainsKey(current
))
969 _services
.Add(current
, item
);
976 internal void RemoveService(Type trackingServiceType
)
978 _services
.Remove(HashHelper
.HashServiceType(trackingServiceType
));
981 internal bool TryGetProfileVersionId(Type trackingServiceType
, out Version profileVersionId
)
983 profileVersionId
= new Version(0, 0);
985 ServiceProfileContainer service
= null;
986 if (_services
.TryGetValue(HashHelper
.HashServiceType(trackingServiceType
), out service
))
988 profileVersionId
= service
.ProfileVersionId
;
994 internal void MakeProfilePrivate(Type trackingServiceType
)
996 ServiceProfileContainer service
= null;
997 if (!_services
.TryGetValue(HashHelper
.HashServiceType(trackingServiceType
), out service
))
998 throw new ArgumentException(ExecutionStringManager
.InvalidTrackingService
);
1000 service
.IsPrivate
= true;
1003 internal bool IsProfilePrivate(Type trackingServiceType
)
1005 ServiceProfileContainer service
= null;
1006 if (!_services
.TryGetValue(HashHelper
.HashServiceType(trackingServiceType
), out service
))
1007 throw new ArgumentException(ExecutionStringManager
.InvalidTrackingService
);
1009 return service
.IsPrivate
;
1012 internal void MakeProfileInstance(Type trackingServiceType
)
1014 ServiceProfileContainer service
= null;
1015 if (!_services
.TryGetValue(HashHelper
.HashServiceType(trackingServiceType
), out service
))
1016 throw new ArgumentException(ExecutionStringManager
.InvalidTrackingService
);
1018 // Can't be instance without being private
1019 service
.IsPrivate
= true;
1020 service
.IsInstance
= true;
1023 internal bool IsProfileInstance(Type trackingServiceType
)
1025 ServiceProfileContainer service
= null;
1026 if (!_services
.TryGetValue(HashHelper
.HashServiceType(trackingServiceType
), out service
))
1027 throw new ArgumentException(ExecutionStringManager
.InvalidTrackingService
);
1029 return service
.IsInstance
;
1032 internal int GetNextEventOrderId()
1036 return ++_eventOrderId
;
1041 internal class ServiceProfileContainer
1043 Version _profileVersionId
= new Version(0, 0);
1044 bool _isPrivate
= false;
1045 bool _isInstance
= false;
1047 protected ServiceProfileContainer() { }
1049 internal ServiceProfileContainer(Version profileVersionId
)
1051 _profileVersionId
= profileVersionId
;
1054 internal Version ProfileVersionId
1056 get { return _profileVersionId; }
1059 internal bool IsPrivate
1061 get { return _isPrivate; }
1062 set { _isPrivate = value; }
1065 internal bool IsInstance
1067 get { return _isInstance; }
1068 set { _isInstance = value; }
1072 #region ISerializable Members
1074 [SecurityPermissionAttribute(SecurityAction
.Demand
, SerializationFormatter
= true)]
1075 public void GetObjectData(System
.Runtime
.Serialization
.SerializationInfo info
, System
.Runtime
.Serialization
.StreamingContext context
)
1077 info
.AddValue("eventOrderId", this._eventOrderId
);
1078 info
.AddValue("services", this._services
.Count
== 0 ? null : this._services
);
1080 private TrackingListenerBroker(System
.Runtime
.Serialization
.SerializationInfo info
, System
.Runtime
.Serialization
.StreamingContext context
)
1082 this._eventOrderId
= info
.GetInt32("eventOrderId");
1083 this._services
= (Dictionary
<Guid
, ServiceProfileContainer
>)info
.GetValue("services", typeof(Dictionary
<Guid
, ServiceProfileContainer
>));
1084 if (this._services
== null)
1085 this._services
= new Dictionary
<Guid
, ServiceProfileContainer
>();
1092 /// Manages profile requests, caching profiles and creating RTTrackingProfile instances.
1094 internal class TrackingProfileManager
1097 // This is a dictionary keyed by tracking service type
1098 // that returns a dictionary that is key by schedule type
1099 // that returns a Set of profile versions for that schedule type
1100 // The set is constrained by VersionId
1101 private Dictionary
<Type
, Dictionary
<Type
, ProfileList
>> _cacheLookup
;
1103 // Protects _cacheLookup
1104 private object _cacheLock
= new object();
1106 // Values assigned in Initialize
1107 private bool _init
= false;
1108 private List
<TrackingService
> _services
= null;
1109 private WorkflowRuntime _runtime
= null;
1111 internal TrackingProfileManager()
1115 /// Clears all entries from the cache by reinitializing the member
1117 public static void ClearCache()
1119 WorkflowRuntime
.ClearTrackingProfileCache();
1122 internal void ClearCacheImpl()
1126 _cacheLookup
= new Dictionary
<Type
, Dictionary
<Type
, ProfileList
>>();
1130 /// Create static state
1132 /// <param name="runtime"></param>
1133 internal void Initialize(WorkflowRuntime runtime
)
1137 if (null == runtime
)
1138 throw new ArgumentException(ExecutionStringManager
.NullEngine
);
1142 // Initialize the cache
1143 // Do this every time the runtime starts/stops to make life easier
1144 // for IProfileNotification tracking services that might have updated
1145 // profiles while we were stopped - we'll go get new versions since nothing is cached
1146 // without them having to fire updated events.
1147 _cacheLookup
= new Dictionary
<Type
, Dictionary
<Type
, ProfileList
>>();
1148 if (null != runtime
.TrackingServices
)
1150 _services
= runtime
.TrackingServices
;
1151 foreach (TrackingService service
in _services
)
1153 if (service
is IProfileNotification
)
1155 ((IProfileNotification
)service
).ProfileUpdated
+= new EventHandler
<ProfileUpdatedEventArgs
>(ProfileUpdated
);
1156 ((IProfileNotification
)service
).ProfileRemoved
+= new EventHandler
<ProfileRemovedEventArgs
>(ProfileRemoved
);
1164 /// Clean up static state
1166 internal void Uninitialize()
1170 if (null != _runtime
)
1172 foreach (TrackingService service
in _services
)
1174 if (service
is IProfileNotification
)
1176 ((IProfileNotification
)service
).ProfileUpdated
-= new EventHandler
<ProfileUpdatedEventArgs
>(ProfileUpdated
);
1177 ((IProfileNotification
)service
).ProfileRemoved
-= new EventHandler
<ProfileRemovedEventArgs
>(ProfileRemoved
);
1187 /// Retrieves the current version of a profile from the specified service
1189 internal RTTrackingProfile
GetProfile(TrackingService service
, Activity schedule
)
1192 throw new ApplicationException(ExecutionStringManager
.TrackingProfileManagerNotInitialized
);
1194 if ((null == service
) || (null == schedule
))
1196 WorkflowTrace
.Tracking
.TraceEvent(TraceEventType
.Error
, 0, ExecutionStringManager
.NullParameters
);
1200 Type workflowType
= schedule
.GetType();
1201 RTTrackingProfile tp
= null;
1202 if (service
is IProfileNotification
)
1205 // If we found the profile in the cache return it, it may be null, this is OK
1206 // (no profile for this service type/schedule type combination)
1207 if (TryGetFromCache(service
.GetType(), workflowType
, out tp
))
1211 // Either we don't have anything in the cache for this schedule/service combination
1212 // or this is a base TrackingService that doesn't notify of profile updates
1213 // Get the profile from the service
1214 TrackingProfile profile
= null;
1216 if (!service
.TryGetProfile(workflowType
, out profile
))
1219 // No profile for this schedule from this service
1220 // RemoveProfile will just mark this service/schedule as not currently having a profile in the cache
1221 RemoveProfile(workflowType
, service
.GetType());
1225 // Check the cache to see if we already have this version
1226 // For TrackingService types this is necessary.
1227 // For IProfileNotification types this is a bit redundant
1228 // but another threadcould have inserted the profile into the cache
1229 // so check again before acquiring the writer lock
1230 if (TryGetFromCache(service
.GetType(), workflowType
, profile
.Version
, out tp
))
1233 // No profile, create it
1234 string xaml
= schedule
.GetValue(Activity
.WorkflowXamlMarkupProperty
) as string;
1235 if (null != xaml
&& xaml
.Length
> 0)
1238 // Never add xaml only workflows to the cache
1239 // Each one must be handled distinctly
1240 return CreateProfile(profile
, schedule
, service
.GetType());
1244 tp
= CreateProfile(profile
, workflowType
, service
.GetType());
1250 // Recheck the cache with exclusive access
1251 RTTrackingProfile tmp
= null;
1252 if (TryGetFromCache(service
.GetType(), workflowType
, profile
.Version
, out tmp
))
1255 // Add it to the cache
1256 if (!AddToCache(tp
, service
.GetType()))
1257 throw new ApplicationException(ExecutionStringManager
.ProfileCacheInsertFailure
);
1263 /// Retrieves the specified version of a profile from the specified service
1265 internal RTTrackingProfile
GetProfile(TrackingService service
, Activity workflow
, Version versionId
)
1267 if (null == service
)
1268 throw new ArgumentNullException("service");
1269 if (null == workflow
)
1270 throw new ArgumentNullException("workflow");
1273 throw new InvalidOperationException(ExecutionStringManager
.TrackingProfileManagerNotInitialized
);
1275 Type workflowType
= workflow
.GetType();
1276 RTTrackingProfile tp
= null;
1278 // Looking for a specific version, see if it is in the cache
1279 if (TryGetFromCache(service
.GetType(), workflowType
, versionId
, out tp
))
1282 TrackingProfile profile
= service
.GetProfile(workflowType
, versionId
);
1284 // No profile, create it
1285 string xaml
= workflow
.GetValue(Activity
.WorkflowXamlMarkupProperty
) as string;
1286 if (null != xaml
&& xaml
.Length
> 0)
1289 // Never add xaml only workflows to the cache
1290 // Each one must be handled distinctly
1291 return CreateProfile(profile
, workflow
, service
.GetType());
1295 tp
= CreateProfile(profile
, workflowType
, service
.GetType());
1301 // Recheck the cache with exclusive access
1302 RTTrackingProfile tmp
= null;
1303 if (TryGetFromCache(service
.GetType(), workflowType
, versionId
, out tmp
))
1306 // Add it to the cache
1307 if (!AddToCache(tp
, service
.GetType()))
1308 throw new ApplicationException(ExecutionStringManager
.ProfileCacheInsertFailure
);
1314 internal RTTrackingProfile
GetProfile(TrackingService service
, Activity workflow
, Guid instanceId
)
1317 // An instance based profile will never be in the cache
1318 TrackingProfile profile
= service
.GetProfile(instanceId
);
1320 if (null == profile
)
1323 return new RTTrackingProfile(profile
, workflow
, service
.GetType());
1326 #region Private Methods
1328 private RTTrackingProfile
CreateProfile(TrackingProfile profile
, Type workflowType
, Type serviceType
)
1331 // Can't use the activity definition that we have here, it may have been updated
1332 // Get the base definition and use it to create the profile.
1333 Activity tmpSchedule
= _runtime
.GetWorkflowDefinition(workflowType
);
1334 return new RTTrackingProfile(profile
, tmpSchedule
, serviceType
);
1336 private RTTrackingProfile
CreateProfile(TrackingProfile profile
, Activity schedule
, Type serviceType
)
1339 // This is called for Xaml only workflows
1340 return new RTTrackingProfile(profile
, schedule
, serviceType
);
1343 /// Add a profile to the cache but do not reset the NoProfiles flag for the schedule type
1345 /// <param name="profile">RTTrackingProfile to add</param>
1346 /// <param name="serviceType">TrackingService type</param>
1347 /// <returns>True if the profile was successfully added; false if not</returns>
1348 private bool AddToCache(RTTrackingProfile profile
, Type serviceType
)
1350 return AddToCache(profile
, serviceType
, false);
1353 /// Adds a profile to the cache and optionally resets the NoProfiles flag for the schedule type
1355 /// <param name="profile">RTTrackingProfile to add</param>
1356 /// <param name="serviceType">TrackingService type</param>
1357 /// <param name="resetNoProfiles">true will reset NoProfiles (to false); false will leave NoProfiles as is</param>
1358 /// <returns>True if the profile was successfully added; false if not</returns>
1359 private bool AddToCache(RTTrackingProfile profile
, Type serviceType
, bool resetNoProfiles
)
1362 // Profile may be null, serviceType may not
1363 if (null == serviceType
)
1368 Dictionary
<Type
, ProfileList
> schedules
= null;
1370 // Get the dictionary for the service type,
1371 // create it if it doesn't exist
1372 if (!_cacheLookup
.TryGetValue(serviceType
, out schedules
))
1374 schedules
= new Dictionary
<Type
, ProfileList
>();
1375 _cacheLookup
.Add(serviceType
, schedules
);
1378 // The the ProfileList for the schedule type,
1379 // create it if it doesn't exist
1380 ProfileList profiles
= null;
1381 if (!schedules
.TryGetValue(profile
.WorkflowType
, out profiles
))
1383 profiles
= new ProfileList();
1384 schedules
.Add(profile
.WorkflowType
, profiles
);
1386 if (resetNoProfiles
)
1387 profiles
.NoProfile
= false;
1388 return profiles
.Profiles
.TryAdd(new CacheItem(profile
));
1392 /// Gets a profile from the cache
1394 private bool TryGetFromCache(Type serviceType
, Type workflowType
, out RTTrackingProfile profile
)
1396 return TryGetFromCache(serviceType
, workflowType
, new Version(0, 0), out profile
); // 0 is an internal signal to get the most current
1399 /// Gets a profile from the cache
1401 private bool TryGetFromCache(Type serviceType
, Type workflowType
, Version versionId
, out RTTrackingProfile profile
)
1404 CacheItem item
= null;
1407 Dictionary
<Type
, ProfileList
> schedules
= null;
1409 if (!_cacheLookup
.TryGetValue(serviceType
, out schedules
))
1412 ProfileList profiles
= null;
1413 if (!schedules
.TryGetValue(workflowType
, out profiles
))
1417 // 0 means get the current version
1418 if (0 == versionId
.Major
)
1421 // Currently the schedule type doesn't have a profile associated to it
1422 if (profiles
.NoProfile
)
1425 if ((null == profiles
.Profiles
) || (0 == profiles
.Profiles
.Count
))
1429 // Current version is highest versionId
1430 // which means it is at the end of the Set
1431 int endPos
= profiles
.Profiles
.Count
- 1;
1433 if (null == profiles
.Profiles
[endPos
])
1436 profile
= profiles
.Profiles
[endPos
].TrackingProfile
;
1441 if ((null == profiles
.Profiles
) || (0 == profiles
.Profiles
.Count
))
1444 if (profiles
.Profiles
.TryGetValue(new CacheItem(workflowType
, versionId
), out item
))
1446 profile
= item
.TrackingProfile
;
1457 #region Event Handlers
1459 /// Listens on ProfileUpdated events from IProfileNotification services
1461 /// <param name="sender">Type of the tracking service sending the update</param>
1462 /// <param name="e">ProfileUpdatedEventArgs containing the new profile and the schedule type</param>
1463 private void ProfileUpdated(object sender
, ProfileUpdatedEventArgs e
)
1466 throw new ArgumentNullException("sender");
1468 Type t
= sender
.GetType();
1470 if (null == e
.WorkflowType
)
1471 throw new ArgumentNullException("e");
1473 if (null == e
.TrackingProfile
)
1475 RemoveProfile(e
.WorkflowType
, t
);
1479 RTTrackingProfile profile
= CreateProfile(e
.TrackingProfile
, e
.WorkflowType
, t
);
1481 // If AddToCache fails this version is already in the cache and we don't care
1482 AddToCache(profile
, t
, true);
1485 private void ProfileRemoved(object sender
, ProfileRemovedEventArgs e
)
1488 throw new ArgumentNullException("sender");
1490 if (null == e
.WorkflowType
)
1491 throw new ArgumentNullException("e");
1493 RemoveProfile(e
.WorkflowType
, sender
.GetType());
1496 private void RemoveProfile(Type workflowType
, Type serviceType
)
1500 Dictionary
<Type
, ProfileList
> schedules
= null;
1502 if (!_cacheLookup
.TryGetValue(serviceType
, out schedules
))
1504 schedules
= new Dictionary
<Type
, ProfileList
>();
1505 _cacheLookup
.Add(serviceType
, schedules
);
1507 ProfileList profiles
= null;
1508 if (!schedules
.TryGetValue(workflowType
, out profiles
))
1510 profiles
= new ProfileList();
1511 schedules
.Add(workflowType
, profiles
);
1514 // Finally indicate that there isn't a profile for this schedule type
1515 // Calling UpdateProfile for this type will result in resetting this field
1516 // regardless of whether the version of the profile passed is in the cache or not
1517 profiles
.NoProfile
= true;
1522 #region Private Classes
1523 private class ProfileList
1525 internal bool NoProfile
= false;
1526 internal Set
<CacheItem
> Profiles
= new Set
<CacheItem
>(5);
1529 private class CacheItem
: IComparable
1531 internal RTTrackingProfile TrackingProfile
= null;
1532 internal DateTime LastAccess
= DateTime
.UtcNow
;
1534 // VersionId and ScheduleType are stored separately from the profile so that they
1535 // can be used to identify the profile if it has been pushed from the cache.
1536 internal Version VersionId
= new Version(0, 0);
1537 internal Type ScheduleType
= null;
1539 internal CacheItem()
1543 internal CacheItem(RTTrackingProfile profile
)
1545 if (null == profile
)
1546 throw new ArgumentNullException("profile");
1548 ScheduleType
= profile
.WorkflowType
;
1550 this.TrackingProfile
= profile
;
1551 VersionId
= profile
.Version
;
1554 internal CacheItem(Type workflowType
, Version versionId
)
1556 VersionId
= versionId
;
1557 ScheduleType
= workflowType
;
1560 #region IComparable Members
1562 public int CompareTo(object obj
)
1564 if (!(obj
is CacheItem
))
1565 throw new ArgumentException(ExecutionStringManager
.InvalidCacheItem
);
1567 CacheItem item
= (CacheItem
)obj
;
1568 if ((VersionId
== item
.VersionId
) && (ScheduleType
== item
.ScheduleType
))
1571 return (VersionId
> item
.VersionId
) ? 1 : -1;
1580 /// Represents a wrapper around a channel and its artifacts, such as its tracking service type and profile
1582 internal class TrackingChannelWrapper
1584 private Type _serviceType
= null, _scheduleType
= null;
1585 private TrackingChannel _channel
= null;
1587 private RTTrackingProfile _profile
= null;
1588 private Version _profileVersionId
;
1590 private TrackingChannelWrapper() { }
1592 public TrackingChannelWrapper(TrackingChannel channel
, Type serviceType
, Type workflowType
, RTTrackingProfile profile
)
1594 _serviceType
= serviceType
;
1595 _scheduleType
= workflowType
;
1598 _profileVersionId
= profile
.Version
;
1601 internal Type TrackingServiceType
1603 get { return _serviceType; }
1606 internal TrackingChannel TrackingChannel
1608 get { return _channel; }
1611 /// Get the tracking profile for the channel
1613 /// <param name="exec">BaseExecutor</param>
1614 /// <returns>RTTrackingProfile</returns>
1615 internal RTTrackingProfile
GetTrackingProfile(WorkflowExecutor skedExec
)
1617 if (null != _profile
)
1620 throw new InvalidOperationException(String
.Format(System
.Globalization
.CultureInfo
.CurrentCulture
, ExecutionStringManager
.NullProfileForChannel
, this._scheduleType
.AssemblyQualifiedName
));
1623 internal void SetTrackingProfile(RTTrackingProfile profile
)
1629 /// Clone the tracking profile stored in the cache and
1631 /// <param name="exec"></param>
1632 internal void MakeProfilePrivate(WorkflowExecutor exec
)
1634 if (null != _profile
)
1637 // If the profile is not already a private copy make it so
1638 if (!_profile
.IsPrivate
)
1640 _profile
= _profile
.Clone();
1641 _profile
.IsPrivate
= true;
1647 // We're not holding a reference to a profile
1648 // so get it from the cache and clone it into a private copy
1649 RTTrackingProfile tmp
= GetTrackingProfile(exec
);
1650 _profile
= tmp
.Clone();
1651 _profile
.IsPrivate
= true;
1655 internal class Set
<T
> : IEnumerable
<T
> where T
: IComparable
1657 List
<T
> list
= null;
1661 list
= new List
<T
>();
1664 public Set(int capacity
)
1666 list
= new List
<T
>(capacity
);
1671 get { return list.Count; }
1674 public void Add(T item
)
1677 if (!Search(item
, out pos
))
1678 list
.Insert(pos
, item
);
1680 throw new ArgumentException(ExecutionStringManager
.ItemAlreadyExist
);
1683 public bool TryAdd(T item
)
1686 if (!Search(item
, out pos
))
1688 list
.Insert(pos
, item
);
1695 public bool Contains(T item
)
1698 return Search(item
, out pos
);
1701 public IEnumerator
<T
> GetEnumerator()
1703 return list
.GetEnumerator();
1706 System
.Collections
.IEnumerator IEnumerable
.GetEnumerator()
1708 return list
.GetEnumerator();
1711 public bool TryGetValue(T item
, out T
value)
1714 if (Search(item
, out pos
))
1726 public T
this[int index
]
1728 get { return list[index]; }
1731 private bool Search(T item
, out int insertPos
)
1740 while (high
- low
> 1)
1742 pos
= (high
+ low
) / 2;
1744 diff
= list
[pos
].CompareTo(item
);
1765 insertPos
= (diff
< 0) ? pos
+ 1 : pos
;
1774 /// Persisted tracking State pertaining to workflow invoking for an individual schedule. There could be multiple called schedules under
1778 internal class TrackingCallingState
1780 #region data members
1781 private IList
<string> callerActivityPathProxy
;
1782 private Guid callerInstanceId
;
1783 private Guid callerContextGuid
;
1784 private Guid callerParentContextGuid
;
1786 #endregion data members
1788 #region Property accessors
1791 /// Activity proxy of the caller/execer activity, if any
1792 /// //@@Undone for Ashishmi: Hold on to ActivityPath Proxy in one of your class impl
1795 internal IList
<string> CallerActivityPathProxy
1797 get { return callerActivityPathProxy; }
1798 set { callerActivityPathProxy = value; }
1802 /// Instance ID of the caller/exec'er schedule, if any
1805 public Guid CallerWorkflowInstanceId
1807 get { return callerInstanceId; }
1808 set { callerInstanceId = value; }
1811 /// Context of the caller's invoke activity
1813 /// <value>int</value>
1814 public Guid CallerContextGuid
1816 get { return callerContextGuid; }
1817 set { callerContextGuid = value; }
1820 /// ParentContext of the caller's invoke activity
1822 /// <value>int</value>
1823 public Guid CallerParentContextGuid
1825 get { return callerParentContextGuid; }
1826 set { callerParentContextGuid = value; }
1829 #endregion Property accessors
1833 internal static class HashHelper
1835 internal static Guid
HashServiceType(Type serviceType
)
1837 return HashServiceType(serviceType
.AssemblyQualifiedName
);
1840 [SuppressMessage("Microsoft.Cryptographic.Standard", "CA5350:MD5CannotBeUsed",
1841 Justification
= "Design has been approved. We are not using MD5 for any security or cryptography purposes but rather as a hash.")]
1842 internal static Guid
HashServiceType(String serviceFullTypeName
)
1847 UnicodeEncoding ue
= new UnicodeEncoding();
1848 data
= ue
.GetBytes(serviceFullTypeName
);
1850 if (AppSettings
.FIPSRequired
)
1852 result
= MD5PInvokeHelper
.CalculateHash(data
);
1856 MD5 md5
= new MD5CryptoServiceProvider();
1857 result
= md5
.ComputeHash(data
);
1860 return new Guid(result
);