2 using System
.Collections
.Generic
;
3 using System
.Collections
;
4 using System
.Diagnostics
;
5 using System
.Reflection
;
8 using System
.IO
.Compression
;
9 using System
.Transactions
;
10 using System
.Workflow
.ComponentModel
;
12 namespace System
.Workflow
.Runtime
14 internal sealed class WorkflowStateRollbackService
16 WorkflowExecutor workflowExecutor
;
18 // cache the revert back data
19 MemoryStream clonedInstanceStateStream
;
20 Activity workflowDefinition
= null;
21 bool isInstanceStateRevertRequested
= false;
23 // revert back notification info
24 string activityQualifiedName
;
25 int activityContextId
;
26 EventArgs callbackData
;
27 EventHandler
<EventArgs
> callbackHandler
;
29 string suspendOnRevertInfo
;
31 Hashtable completedContextActivities
= new Hashtable();
33 public WorkflowStateRollbackService(WorkflowExecutor workflowExecutor
)
35 this.workflowExecutor
= workflowExecutor
;
38 internal bool IsInstanceStateRevertRequested
40 get { return this.isInstanceStateRevertRequested; }
43 internal void CheckpointInstanceState()
45 Debug
.Assert(this.workflowExecutor
.InstanceId
!= null, "instance id null at checkpoint time");
47 // serialize the instance state
48 this.clonedInstanceStateStream
= new MemoryStream(10240);
49 this.workflowExecutor
.RootActivity
.Save(this.clonedInstanceStateStream
);
50 this.workflowDefinition
= this.workflowExecutor
.WorkflowDefinition
;
51 this.completedContextActivities
= (Hashtable
)this.workflowExecutor
.CompletedContextActivities
.Clone();
52 this.clonedInstanceStateStream
.Position
= 0;
55 internal void RequestRevertToCheckpointState(Activity currentActivity
, EventHandler
<EventArgs
> callbackHandler
, EventArgs callbackData
, bool suspendOnRevert
, string suspendInfo
)
57 if (this.clonedInstanceStateStream
== null)
58 throw new InvalidOperationException(ExecutionStringManager
.InvalidRevertRequest
);
60 // cache the after revert information
61 this.activityContextId
= ContextActivityUtils
.ContextId(ContextActivityUtils
.ContextActivity(currentActivity
));
62 this.activityQualifiedName
= currentActivity
.QualifiedName
;
63 this.callbackData
= callbackData
;
64 this.callbackHandler
= callbackHandler
;
65 this.suspendOnRevert
= suspendOnRevert
;
66 this.suspendOnRevertInfo
= suspendInfo
;
68 // ask scheduler to stop
69 this.isInstanceStateRevertRequested
= true;
70 this.workflowExecutor
.Scheduler
.CanRun
= false;
73 internal void DisposeCheckpointState()
75 this.clonedInstanceStateStream
= null;
78 internal void RevertToCheckpointState()
80 Debug
.Assert(this.clonedInstanceStateStream
!= null, "cloned instance-state stream null at restore time");
82 // deserialize only on first access
83 Activity clonedRootActivity
= null;
84 this.clonedInstanceStateStream
.Position
= 0;
85 using (RuntimeEnvironment runtimeEnv
= new RuntimeEnvironment(this.workflowExecutor
.WorkflowRuntime
))
87 clonedRootActivity
= Activity
.Load(this.clonedInstanceStateStream
, (Activity
)this.workflowDefinition
);
89 Debug
.Assert(clonedRootActivity
!= null);
91 // Set the trackingListenerBroker before initializing the executor so the tracking
92 // runtime gets a reference to the correct object
93 clonedRootActivity
.SetValue(WorkflowExecutor
.TrackingListenerBrokerProperty
, workflowExecutor
.RootActivity
.GetValue(WorkflowExecutor
.TrackingListenerBrokerProperty
));
95 // create the new workflowExecutor
96 WorkflowExecutor newWorkflowExecutor
= new WorkflowExecutor(Guid
.Empty
); // use a dummy guid while swapping executors
97 newWorkflowExecutor
.Initialize(clonedRootActivity
, this.workflowExecutor
.WorkflowRuntime
, this.workflowExecutor
);
99 // enqueue the activity notifier
100 Activity activityContext
= newWorkflowExecutor
.GetContextActivityForId(this.activityContextId
);
101 Activity activity
= activityContext
.GetActivityByName(this.activityQualifiedName
);
102 using (new ServiceEnvironment(activity
))
104 using (newWorkflowExecutor
.SetCurrentActivity(activity
))
106 using (ActivityExecutionContext executionContext
= new ActivityExecutionContext(activity
))
107 executionContext
.Invoke
<EventArgs
>(this.callbackHandler
, this.callbackData
);
111 // Push the batch item ordering id to the new instance
112 newWorkflowExecutor
.BatchCollection
.WorkItemOrderId
= this.workflowExecutor
.BatchCollection
.WorkItemOrderId
;
113 // replace pending batch items
114 foreach (KeyValuePair
<object, WorkBatch
> batch
in this.workflowExecutor
.BatchCollection
)
116 batch
.Value
.SetWorkBatchCollection(newWorkflowExecutor
.BatchCollection
);
117 Activity oldActivity
= batch
.Key
as Activity
;
118 // no need to add the transient state batch
119 if (oldActivity
!= null)
121 Activity newactivity
= activityContext
.GetActivityByName(oldActivity
.QualifiedName
);
122 newWorkflowExecutor
.BatchCollection
.Add(newactivity
, batch
.Value
);
125 this.workflowExecutor
.BatchCollection
.Clear();
127 Debug
.Assert(this.completedContextActivities
!= null);
128 newWorkflowExecutor
.CompletedContextActivities
= this.completedContextActivities
;
130 // replace with the WorkflowRuntime
131 Debug
.Assert(this.workflowExecutor
.IsInstanceValid
);
132 this.workflowExecutor
.WorkflowRuntime
.ReplaceWorkflowExecutor(this.workflowExecutor
.InstanceId
, this.workflowExecutor
, newWorkflowExecutor
);
134 // now resume or suspend the scheduler as needed
135 if (!this.suspendOnRevert
)
137 // get the new one going
138 newWorkflowExecutor
.Scheduler
.Resume();
142 // this call will be old scheduler's thread
143 newWorkflowExecutor
.SuspendOnIdle(this.suspendOnRevertInfo
);
145 DisposeCheckpointState();