1 
2 using System;
3 using System.Collections;
4 using System.Collections.Generic;
5 using System.Collections.ObjectModel;
6 using System.Text;
7 using System.Xml;
8 using System.Xml.Schema;
9 using System.IO;
10 using System.Reflection;
11 using System.Diagnostics;
12 using System.Runtime.Serialization;
13 using System.Security.Permissions;
14 using System.Globalization;
15 
16 //using System.Workflow.Activities;
17 using System.Workflow.ComponentModel;
18 using System.Workflow.Runtime;
19 using System.Workflow.Runtime.Hosting;
20 using Hosting = System.Workflow.Runtime.Hosting;
21 using System.Workflow.Runtime.Tracking;
22 
23 
24 namespace System.Workflow.Runtime
25 {
26     /// <summary>
27     /// RTTrackingProfile contains functionality specific to the runtime such as
28     /// trackpoint and location matching and caching, cloning, handling dynamic updates...
29     /// </summary>
30     internal class RTTrackingProfile : ICloneable // ICloneable is deprecated
31     {
32         #region Private Data Members
33         //
34         // Client defined profile
35         private TrackingProfile _profile = null;
36         //
37         // Type of the workflow that this profile is associated to
38         private Type _workflowType = null;
39         private Type _serviceType = null;
40         //
41         // List of qualified ids and the trackpoints that declared themselves as matches during static examination
42         private Dictionary<string, List<ActivityTrackPointCacheItem>> _activities = new Dictionary<string, List<ActivityTrackPointCacheItem>>();
43         private List<string> _activitiesIgnore = new List<string>();
44 
45         private Dictionary<string, List<UserTrackPoint>> _user = new Dictionary<string, List<UserTrackPoint>>();
46         private List<string> _userIgnore = new List<string>();
47         //
48         // Indicates that the RTTrackingProfile instance is private and is safe to modify for a specific instance
49         private bool _isPrivate = false;
50         //
51         // Indicates if a dynamic update is in-flight
52         private bool _pendingWorkflowChange = false;
53         //
54         // The changes for a dynamic update
55         private IList<WorkflowChangeAction> _pendingChanges = null;
56         //
57         // Activities (including those that are being added) can start executing while a dynamic update is pending
58         // These cannot be added to the main cache until the update succeeds because the update might roll back.
59         // However since we have to search for matching track points we might as well save that work.
60         // This list will be copied into the main cache if the dynamic update completes successfully
61         private Dictionary<string, List<ActivityTrackPointCacheItem>> _dynamicActivities = null;
62         private List<string> _dynamicActivitiesIgnore = null;
63 
64         private Dictionary<string, List<UserTrackPoint>> _dynamicUser = null;
65         private List<string> _dynamicUserIgnore = null;
66 
67         #endregion
68 
69         #region Constructors
70         /// <summary>
71         /// Default constructor
72         /// </summary>
RTTrackingProfile()73         protected RTTrackingProfile()
74         {
75         }
76         /// <summary>
77         /// Primary constructor
78         /// </summary>
79         /// <param name="root"></param>
80         /// <param name="workflowType"></param>
81         /// <param name="profile"></param>
RTTrackingProfile(TrackingProfile profile, Activity root, Type serviceType)82         internal RTTrackingProfile(TrackingProfile profile, Activity root, Type serviceType)
83         {
84             if (null == profile)
85                 throw new ArgumentNullException("profile");
86             if (null == root)
87                 throw new ArgumentNullException("root");
88             if (null == serviceType)
89                 throw new ArgumentNullException("serviceType");
90 
91             _workflowType = root.GetType();
92             _serviceType = serviceType;
93             //
94             // "Clone" a private copy in case the tracking service holds a reference to
95             // the profile it gave us and attempts to modify it at a later point
96             TrackingProfileSerializer tps = new TrackingProfileSerializer();
97 
98             StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
99             StringReader reader = null;
100 
101             TrackingProfile privateProfile = null;
102 
103             try
104             {
105                 //
106                 // Let exceptions bubble back to the tracking service -
107                 // the profile must be valid per the schema.
108                 tps.Serialize(writer, profile);
109                 reader = new StringReader(writer.ToString());
110                 privateProfile = tps.Deserialize(reader);
111             }
112             finally
113             {
114                 if (null != reader)
115                     reader.Close();
116 
117                 if (null != writer)
118                     writer.Close();
119             }
120             _profile = privateProfile;
121 
122             CheckAllActivities((Activity)root);
123         }
124 
125         /// <summary>
126         /// Constructor used for cloning.
127         /// </summary>
128         /// <param name="runtimeProfile">RTTrackingProfile to clone</param>
129         /// <remarks>All members are shallow copied!  Use MakePrivate to deep copy after cloning.</remarks>
RTTrackingProfile(RTTrackingProfile runtimeProfile)130         private RTTrackingProfile(RTTrackingProfile runtimeProfile)
131         {
132             //
133             // Shallow copy
134             _profile = runtimeProfile._profile;
135             _isPrivate = runtimeProfile._isPrivate;
136             _pendingChanges = runtimeProfile._pendingChanges;
137             _pendingWorkflowChange = runtimeProfile._pendingWorkflowChange;
138             _workflowType = runtimeProfile._workflowType;
139             //
140             // Deep copy the cache.  Items in the cache can
141             // be shared but the cache themselves cannot as they may be modified
142             //
143             // Activity match and ignore cache
144             _activities = new Dictionary<string, List<ActivityTrackPointCacheItem>>(runtimeProfile._activities.Count);
145             foreach (KeyValuePair<string, List<ActivityTrackPointCacheItem>> kvp in runtimeProfile._activities)
146                 _activities.Add(kvp.Key, runtimeProfile._activities[kvp.Key]);
147 
148             _activitiesIgnore = new List<string>(runtimeProfile._activitiesIgnore);
149             //
150             // Pending dynamic update activity match and ignore cache
151             if (null != runtimeProfile._dynamicActivities)
152             {
153                 _dynamicActivities = new Dictionary<string, List<ActivityTrackPointCacheItem>>(runtimeProfile._dynamicActivities.Count);
154                 foreach (KeyValuePair<string, List<ActivityTrackPointCacheItem>> kvp in runtimeProfile._dynamicActivities)
155                     _dynamicActivities.Add(kvp.Key, runtimeProfile._dynamicActivities[kvp.Key]);
156             }
157 
158             if (null != runtimeProfile._dynamicActivitiesIgnore)
159                 _dynamicActivitiesIgnore = new List<string>(runtimeProfile._dynamicActivitiesIgnore);
160             //
161             // User event match and ignore cache
162             _user = new Dictionary<string, List<UserTrackPoint>>(runtimeProfile._user.Count);
163             foreach (KeyValuePair<string, List<UserTrackPoint>> kvp in runtimeProfile._user)
164                 _user.Add(kvp.Key, runtimeProfile._user[kvp.Key]);
165 
166             _userIgnore = new List<string>(runtimeProfile._userIgnore);
167             //
168             // Pending dynamic update activity match and ignore cache
169             if (null != runtimeProfile._dynamicUser)
170             {
171                 _dynamicUser = new Dictionary<string, List<UserTrackPoint>>(runtimeProfile._dynamicUser.Count);
172                 foreach (KeyValuePair<string, List<UserTrackPoint>> kvp in runtimeProfile._dynamicUser)
173                     _dynamicUser.Add(kvp.Key, kvp.Value);
174             }
175 
176             if (null != runtimeProfile._dynamicUserIgnore)
177                 _dynamicUserIgnore = new List<string>(runtimeProfile._dynamicUserIgnore);
178         }
179 
180         #endregion
181 
182         #region Properties
183         /// <summary>
184         /// Indicates if the profile is specific to an individual instance.
185         /// </summary>
186         internal bool IsPrivate
187         {
188             get { return _isPrivate; }
189             set
190             {
191                 if (!(value) && (_isPrivate))
192                     throw new InvalidOperationException(ExecutionStringManager.CannotResetIsPrivate);
193 
194                 _isPrivate = value;
195             }
196         }
197         /// <summary>
198         /// Type of workflow to which this profile is associated
199         /// </summary>
200         internal Type WorkflowType
201         {
202             get { return _workflowType; }
203         }
204 
205         /// <summary>
206         /// Version of the profile
207         /// </summary>
208         internal Version Version
209         {
210             get { return _profile.Version; }
211         }
212 
213         #endregion
214 
215         #region Internal Methods for Listeners
216 
TryTrackActivityEvent(Activity activity, ActivityExecutionStatus status, IServiceProvider provider, ActivityTrackingRecord record)217         internal bool TryTrackActivityEvent(Activity activity, ActivityExecutionStatus status, IServiceProvider provider, ActivityTrackingRecord record)
218         {
219             List<ActivityTrackPointCacheItem> points;
220             //
221             // Check the match caches.
222             if (TryGetCacheItems(activity, out points))
223             {
224                 bool ret = false;
225                 foreach (ActivityTrackPointCacheItem item in points)
226                 {
227                     if (item.HasLocationConditions)
228                     {
229                         if (!item.Point.IsMatch(activity, status))
230                             continue;
231                     }
232 
233                     if (item.Events.Contains(status))
234                     {
235                         ret = true;
236                         item.Point.Track(activity, provider, record.Body);
237                         record.Annotations.AddRange(item.Point.Annotations);
238                     }
239                 }
240                 return ret;
241             }
242             return false;
243         }
244 
TryTrackUserEvent(Activity activity, string keyName, object argument, WorkflowExecutor exec, UserTrackingRecord record)245         internal bool TryTrackUserEvent(Activity activity, string keyName, object argument, WorkflowExecutor exec, UserTrackingRecord record)
246         {
247             List<UserTrackPoint> points;
248             if (TryGetCacheItems(activity, out points))
249             {
250                 bool ret = false;
251                 foreach (UserTrackPoint point in points)
252                 {
253                     if (point.IsMatch(activity, keyName, argument))
254                     {
255                         ret = true;
256                         point.Track(activity, argument, exec, record.Body);
257                         record.Annotations.AddRange(point.Annotations);
258                     }
259                 }
260                 return ret;
261             }
262             return false;
263         }
264 
TryTrackInstanceEvent(TrackingWorkflowEvent status, WorkflowTrackingRecord record)265         internal bool TryTrackInstanceEvent(TrackingWorkflowEvent status, WorkflowTrackingRecord record)
266         {
267             bool track = false;
268             foreach (WorkflowTrackPoint point in _profile.WorkflowTrackPoints)
269             {
270                 if (point.IsMatch(status))
271                 {
272                     record.Annotations.AddRange(point.Annotations);
273                     track = true;
274                 }
275             }
276             return track;
277         }
278 
279 
280         /// <summary>
281         /// Called by TrackingListener to determine if a subscription is needed for an activity.
282         /// Also used as an entry point for dynamically building cache entries for dynamically added activities.
283         /// </summary>
284         /// <param name="activity"></param>
285         /// <param name="exec"></param>
286         /// <returns></returns>
ActivitySubscriptionNeeded(Activity activity)287         internal bool ActivitySubscriptionNeeded(Activity activity)
288         {
289             List<ActivityTrackPointCacheItem> points = null;
290             if ((!_pendingWorkflowChange) || ((_pendingWorkflowChange) && (!IsPendingUpdateActivity(activity, true))))
291             {
292                 //
293                 // A dynamic update is not in progress or
294                 // the activity is not part of the dynamic update.
295                 // The main cache has all matching track points
296                 //
297                 //
298                 bool retry = true;
299                 while (retry)
300                 {
301                     if (_activitiesIgnore.Contains(activity.QualifiedName))
302                         return false;
303 
304                     if (_activities.TryGetValue(activity.QualifiedName, out points))
305                         return true;
306                     else
307                         //
308                         // This activity isn't in either cache, look it up in the profile and add to cache
309                         CheckActivity(activity);
310                 }
311                 return false;
312             }
313             else
314             {
315                 //
316                 // Dynamic update is in progress and this activity is being added as part of the update
317                 // Search the profile for matching track points and add them to the dynamic cache
318                 // (copied to the main cache at the successful completion of the update)
319                 // Don't go through CheckActivity because that adds to the main cache
320                 List<UserTrackPoint> user = null;
321                 if (CreateCacheItems(activity, out user))
322                     CacheInsertUpdatePending(activity.QualifiedName, user);
323                 else
324                     _dynamicUserIgnore.Add(activity.QualifiedName);
325 
326                 if (CreateCacheItems(activity, out points))
327                 {
328                     CacheInsertUpdatePending(activity.QualifiedName, points);
329                     return true;
330                 }
331                 else
332                 {
333                     _dynamicActivitiesIgnore.Add(activity.QualifiedName);
334                     return false;
335                 }
336             }
337         }
338 
WorkflowChangeBegin(IList<WorkflowChangeAction> changeActions)339         public void WorkflowChangeBegin(IList<WorkflowChangeAction> changeActions)
340         {
341             Debug.Assert(!_pendingWorkflowChange, "_pendingWorkflowChange should be false.");
342             if (_pendingWorkflowChange)
343                 throw new InvalidOperationException(ExecutionStringManager.DynamicUpdateIsNotPending);
344 
345             if (!_isPrivate)
346                 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);
347             //
348             // Initialize the temp dictionary for activities that are spun up during the update process
349             // If the update succeeds we'll copy these to the main _subscriptions dictionary.
350             _dynamicActivities = new Dictionary<string, List<ActivityTrackPointCacheItem>>();
351             _dynamicActivitiesIgnore = new List<string>();
352 
353             _dynamicUser = new Dictionary<string, List<UserTrackPoint>>();
354             _dynamicUserIgnore = new List<string>();
355 
356             _pendingChanges = changeActions;
357             _pendingWorkflowChange = true;
358         }
359 
WorkflowChangeCommit()360         public void WorkflowChangeCommit()
361         {
362             Debug.Assert(_pendingWorkflowChange, "Workflow change is not pending - no change to commit");
363 
364             if (!_pendingWorkflowChange)
365                 return;
366 
367             if (!_isPrivate)
368                 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);
369             //
370             // Remove items that have been deleted by this update
371             // Must do all removes first as there may be a new action
372             // with the same qid as a previous action that is being removed
373             if (null != _pendingChanges)
374             {
375                 foreach (WorkflowChangeAction action in _pendingChanges)
376                 {
377                     if (action is RemovedActivityAction)
378                     {
379                         //
380                         // Remove all references to this activity that might exist in our caches
381                         string qId = ((RemovedActivityAction)action).OriginalRemovedActivity.QualifiedName;
382                         _activities.Remove(qId);
383                         _activitiesIgnore.Remove(qId);
384                         _user.Remove(qId);
385                         _userIgnore.Remove(qId);
386                     }
387                 }
388             }
389             //
390             // Copy any pending cache items to the regular activity track point cache
391             if ((null != _dynamicActivities) && (_dynamicActivities.Count > 0))
392                 foreach (KeyValuePair<string, List<ActivityTrackPointCacheItem>> kvp in _dynamicActivities)
393                     _activities.Add(kvp.Key, kvp.Value);
394 
395             if ((null != _dynamicActivitiesIgnore) && (_dynamicActivitiesIgnore.Count > 0))
396                 _activitiesIgnore.AddRange(_dynamicActivitiesIgnore);
397 
398             if ((null != _dynamicUser) && (_dynamicUser.Count > 0))
399                 foreach (KeyValuePair<string, List<UserTrackPoint>> kvp in _dynamicUser)
400                     _user.Add(kvp.Key, kvp.Value);
401 
402             if ((null != _dynamicUserIgnore) && (_dynamicUserIgnore.Count > 0))
403                 _userIgnore.AddRange(_dynamicUserIgnore);
404             //
405             // All done, clean up
406             _dynamicActivities = null;
407             _dynamicActivitiesIgnore = null;
408             _dynamicUser = null;
409             _dynamicUserIgnore = null;
410             _pendingChanges = null;
411             _pendingWorkflowChange = false;
412         }
413 
WorkflowChangeRollback()414         public void WorkflowChangeRollback()
415         {
416             //
417             // Just clean up, there isn't any work to rollback because
418             // any subscriptions that may have been added for a pending add activity
419             // won't ever be hit as the activities haven't been added to the tree.
420             _dynamicActivities = null;
421             _dynamicActivitiesIgnore = null;
422             _dynamicUser = null;
423             _dynamicUserIgnore = null;
424             _pendingChanges = null;
425             _pendingWorkflowChange = false;
426         }
427 
428         #endregion
429 
430         #region Private Cache Methods
431 
432         /// <summary>
433         /// Create the static qualifiedid to trackpoint map
434         /// </summary>
435         /// <param name="root"></param>
CheckAllActivities(Activity activity)436         private void CheckAllActivities(Activity activity)
437         {
438             CheckActivity((Activity)activity);
439             //
440             // Walk down the activity tree
441             // Use EnabledActivities to get invisible activities
442             // EnabledActivities will not return commented activities
443             if (activity is CompositeActivity)
444                 foreach (Activity a in GetAllEnabledActivities((CompositeActivity)activity))
445                     CheckAllActivities(a);
446         }
447 
448         /// <summary>
449         /// Recursively walk the activity tree and find all track points that match each activity
450         /// </summary>
451         /// <param name="activity"></param>
CheckActivity(Activity activity)452         private void CheckActivity(Activity activity)
453         {
454             //
455             // Build caches of activity status change events
456             string qId = activity.QualifiedName;
457             List<ActivityTrackPointCacheItem> activities = null;
458             if (CreateCacheItems(activity, out activities))
459                 CacheInsert(qId, activities);
460             else
461                 _activitiesIgnore.Add(qId);
462 
463             //
464             // Build caches of user events
465             List<UserTrackPoint> user = null;
466             if (CreateCacheItems(activity, out user))
467                 CacheInsert(qId, user);
468             else
469                 _userIgnore.Add(qId);
470         }
471 
472         /// <summary>
473         /// Find all trackpoints that match an activity.
474         /// </summary>
475         /// <param name="activity">Activity for which to determine subscription needs</param>
476         /// <param name="includes">List to be populated with matching track points</param>
477         /// <returns>true if a subscription is needed; false if not</returns>
CreateCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> includes)478         private bool CreateCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> includes)
479         {
480             includes = new List<ActivityTrackPointCacheItem>();
481             //
482             // Check if we have any trackpoints that match this activity
483             foreach (ActivityTrackPoint point in _profile.ActivityTrackPoints)
484             {
485                 List<ActivityExecutionStatus> events;
486                 bool hasCondition = false;
487                 if (point.IsMatch(activity, out events, out hasCondition))
488                     includes.Add(new ActivityTrackPointCacheItem(point, events, hasCondition));
489             }
490 
491             return (includes.Count > 0);
492         }
493 
494         /// <summary>
495         /// Find all trackpoints that match user events for an activity.
496         /// </summary>
497         /// <param name="activity">Activity for which to determine subscription needs</param>
498         /// <param name="includes">List to be populated with matching track points</param>
499         /// <returns>true if a subscription is needed; false if not</returns>
CreateCacheItems(Activity activity, out List<UserTrackPoint> includes)500         private bool CreateCacheItems(Activity activity, out List<UserTrackPoint> includes)
501         {
502             includes = new List<UserTrackPoint>();
503             //
504             // Check if we have any trackpoints that match this activity
505             foreach (UserTrackPoint point in _profile.UserTrackPoints)
506             {
507                 if (point.IsMatch(activity))
508                     includes.Add(point);
509             }
510 
511             return (includes.Count > 0);
512         }
513 
CacheInsert(string qualifiedID, List<ActivityTrackPointCacheItem> points)514         private void CacheInsert(string qualifiedID, List<ActivityTrackPointCacheItem> points)
515         {
516             //
517             // Check to make sure the item isn't in the dictionary
518             // If not add all track points
519             Debug.Assert(!_activities.ContainsKey(qualifiedID), "QualifiedName is already in the activities cache");
520             if (_activities.ContainsKey(qualifiedID))
521                 throw new InvalidOperationException(ExecutionStringManager.RTProfileActCacheDupKey);
522 
523             foreach (ActivityTrackPointCacheItem point in points)
524                 CacheInsert(qualifiedID, point);
525         }
526 
CacheInsert(string qualifiedID, List<UserTrackPoint> points)527         private void CacheInsert(string qualifiedID, List<UserTrackPoint> points)
528         {
529             //
530             // Check to make sure the item isn't in the dictionary
531             // If not add all track points
532             Debug.Assert(!_user.ContainsKey(qualifiedID), "QualifiedName is already in the user cache");
533             if (_user.ContainsKey(qualifiedID))
534                 throw new InvalidOperationException(ExecutionStringManager.RTProfileActCacheDupKey);
535 
536             foreach (UserTrackPoint point in points)
537                 CacheInsert(qualifiedID, point);
538         }
539 
CacheInsert(string qualifiedID, ActivityTrackPointCacheItem point)540         private void CacheInsert(string qualifiedID, ActivityTrackPointCacheItem point)
541         {
542             List<ActivityTrackPointCacheItem> points = null;
543 
544             if (!_activities.TryGetValue(qualifiedID, out points))
545             {
546                 points = new List<ActivityTrackPointCacheItem>();
547                 _activities.Add(qualifiedID, points);
548             }
549             points.Add(point);
550         }
551 
CacheInsert(string qualifiedID, UserTrackPoint point)552         private void CacheInsert(string qualifiedID, UserTrackPoint point)
553         {
554             List<UserTrackPoint> points = null;
555 
556             if (!_user.TryGetValue(qualifiedID, out points))
557             {
558                 points = new List<UserTrackPoint>();
559                 _user.Add(qualifiedID, points);
560             }
561             points.Add(point);
562         }
563 
CacheInsertUpdatePending(string qualifiedID, List<ActivityTrackPointCacheItem> points)564         private void CacheInsertUpdatePending(string qualifiedID, List<ActivityTrackPointCacheItem> points)
565         {
566             //
567             // The activity has been added during a pending dynamic change
568             // add it to a temporary lookup which will be copied to real cache
569             // when the dynamic update commits.
570             if ((!_isPrivate) || (!_pendingWorkflowChange))
571                 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);
572 
573             if (null == _dynamicActivities)
574                 throw new InvalidOperationException(ExecutionStringManager.RTProfileDynamicActCacheIsNull);
575 
576             List<ActivityTrackPointCacheItem> tmp = null;
577             if (!_dynamicActivities.TryGetValue(qualifiedID, out tmp))
578             {
579                 tmp = new List<ActivityTrackPointCacheItem>();
580                 _dynamicActivities.Add(qualifiedID, tmp);
581             }
582 
583             foreach (ActivityTrackPointCacheItem point in points)
584                 tmp.Add(point);
585         }
586 
TryGetCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> points)587         private bool TryGetCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> points)
588         {
589             points = null;
590             if ((!_pendingWorkflowChange) || ((_pendingWorkflowChange) && (!IsPendingUpdateActivity(activity, true))))
591             {
592                 //
593                 // A dynamic update is not in progress or this activity
594                 // is not being added by the current dynamic update.
595                 // The main cache holds all matching track points
596                 return _activities.TryGetValue(activity.QualifiedName, out points);
597             }
598             else
599             {
600                 //
601                 // Dynamic update is in progress
602                 return _dynamicActivities.TryGetValue(activity.QualifiedName, out points);
603             }
604         }
605 
CacheInsertUpdatePending(string qualifiedID, List<UserTrackPoint> points)606         private void CacheInsertUpdatePending(string qualifiedID, List<UserTrackPoint> points)
607         {
608             //
609             // The activity has been added during a pending dynamic change
610             // add it to a temporary lookup which will be copied to real cache
611             // when the dynamic update commits.
612             if ((!_isPrivate) || (!_pendingWorkflowChange))
613                 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);
614 
615             if (null == _dynamicUser)
616                 throw new InvalidOperationException(ExecutionStringManager.RTProfileDynamicActCacheIsNull);
617 
618             List<UserTrackPoint> tmp = null;
619             if (!_dynamicUser.TryGetValue(qualifiedID, out tmp))
620             {
621                 tmp = new List<UserTrackPoint>();
622                 _dynamicUser.Add(qualifiedID, tmp);
623             }
624 
625             foreach (UserTrackPoint point in points)
626                 tmp.Add(point);
627         }
628 
TryGetCacheItems(Activity activity, out List<UserTrackPoint> points)629         private bool TryGetCacheItems(Activity activity, out List<UserTrackPoint> points)
630         {
631             points = null;
632             if ((!_pendingWorkflowChange) || ((_pendingWorkflowChange) && (!IsPendingUpdateActivity(activity, true))))
633             {
634                 //
635                 // A dynamic update is not in progress or this activity
636                 // is not being added by the current dynamic update.
637                 // The main cache holds all matching track points
638                 return _user.TryGetValue(activity.QualifiedName, out points);
639             }
640             else
641             {
642                 //
643                 // Dynamic update is in progress
644                 return _dynamicUser.TryGetValue(activity.QualifiedName, out points);
645             }
646         }
647         #endregion
648 
649         #region Private Methods
650 
651 
652         // This function returns all the executable activities including secondary flow activities.
GetAllEnabledActivities(CompositeActivity compositeActivity)653         public IList<Activity> GetAllEnabledActivities(CompositeActivity compositeActivity)
654         {
655             if (compositeActivity == null)
656                 throw new ArgumentNullException("compositeActivity");
657 
658             List<Activity> allActivities = new List<Activity>(compositeActivity.EnabledActivities);
659 
660             foreach (Activity secondaryFlowActivity in ((ISupportAlternateFlow)compositeActivity).AlternateFlowActivities)
661             {
662                 if (!allActivities.Contains(secondaryFlowActivity))
663                     allActivities.Add(secondaryFlowActivity);
664             }
665 
666             return allActivities;
667         }
668 
IsPendingUpdateActivity(Activity activity, bool addedOnly)669         private bool IsPendingUpdateActivity(Activity activity, bool addedOnly)
670         {
671             //
672             // If we don't have an update going on this method isn't valid
673             if ((!_isPrivate) || (!_pendingWorkflowChange))
674                 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate);
675             //
676             // if we don't have any changes we're done
677             if ((null == _pendingChanges || _pendingChanges.Count <= 0))
678                 return false;
679 
680             foreach (WorkflowChangeAction action in _pendingChanges)
681             {
682                 string qualifiedId = null;
683                 if (action is ActivityChangeAction)
684                 {
685                     if (action is AddedActivityAction)
686                     {
687                         qualifiedId = ((AddedActivityAction)action).AddedActivity.QualifiedName;
688                     }
689                     else if (action is RemovedActivityAction)
690                     {
691                         if (!addedOnly)
692                             qualifiedId = ((RemovedActivityAction)action).OriginalRemovedActivity.QualifiedName;
693                     }
694                     else
695                     {
696                         Debug.Assert(false, ExecutionStringManager.UnknownActivityActionType);
697                     }
698 
699                     if ((null != qualifiedId)
700                         && (0 == String.Compare(activity.QualifiedName, qualifiedId, StringComparison.Ordinal)))
701                     {
702                         return true;
703                     }
704                 }
705             }
706             return false;
707         }
708 
709         #endregion
710 
711         #region ICloneable Members
712 
ICloneable.Clone()713         object ICloneable.Clone()
714         {
715             return this.Clone();
716         }
717 
Clone()718         internal RTTrackingProfile Clone()
719         {
720             return new RTTrackingProfile(this);
721         }
722 
723         #endregion
724 
725         #region Contained Types
726 
727         private struct ActivityTrackPointCacheItem
728         {
ActivityTrackPointCacheItemSystem.Workflow.Runtime.RTTrackingProfile.ActivityTrackPointCacheItem729             internal ActivityTrackPointCacheItem(ActivityTrackPoint point, List<ActivityExecutionStatus> events, bool hasConditions)
730             {
731                 if (null == point)
732                     throw new ArgumentNullException("point");
733                 if (null == events)
734                     throw new ArgumentNullException("events");
735 
736                 Point = point;
737                 Events = events;
738                 HasLocationConditions = hasConditions;
739             }
740 
741             internal ActivityTrackPoint Point;
742             internal List<ActivityExecutionStatus> Events;
743             internal bool HasLocationConditions;
744         }
745 
746         #endregion
747     }
748 }
749