|
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
namespace System.Activities.Statements
{
using System;
using System.Activities.DynamicUpdate;
using System.Collections.Generic;
using System.Runtime;
using System.Collections.ObjectModel;
sealed class CompensationParticipant : NativeActivity
{
InArgument<long> compensationId;
Variable<CompensationToken> currentCompensationToken;
public CompensationParticipant(Variable<long> compensationId)
: base()
{
this.compensationId = compensationId;
this.currentCompensationToken = new Variable<CompensationToken>();
DefaultCompensation = new DefaultCompensation()
{
Target = new InArgument<CompensationToken>(this.currentCompensationToken),
};
DefaultConfirmation = new DefaultConfirmation()
{
Target = new InArgument<CompensationToken>(this.currentCompensationToken),
};
}
public Activity CompensationHandler
{
get;
set;
}
public Activity ConfirmationHandler
{
get;
set;
}
public Activity CancellationHandler
{
get;
set;
}
Activity DefaultCompensation
{
get;
set;
}
Activity DefaultConfirmation
{
get;
set;
}
protected override bool CanInduceIdle
{
get
{
return true;
}
}
protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity)
{
metadata.AllowUpdateInsideThisActivity();
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.SetImplementationVariablesCollection(
new Collection<Variable>
{
this.currentCompensationToken,
});
Collection<Activity> children = new Collection<Activity>();
if (this.CompensationHandler != null)
{
children.Add(CompensationHandler);
}
if (this.ConfirmationHandler != null)
{
children.Add(ConfirmationHandler);
}
if (this.CancellationHandler != null)
{
children.Add(CancellationHandler);
}
metadata.SetChildrenCollection(children);
Collection<Activity> implementationChildren = new Collection<Activity>();
Fx.Assert(DefaultCompensation != null, "DefaultCompensation must be valid");
implementationChildren.Add(DefaultCompensation);
Fx.Assert(DefaultConfirmation != null, "DefaultConfirmation must be valid");
implementationChildren.Add(DefaultConfirmation);
metadata.SetImplementationChildrenCollection(implementationChildren);
RuntimeArgument compensationIdArgument = new RuntimeArgument("CompensationId", typeof(long), ArgumentDirection.In);
metadata.Bind(this.compensationId, compensationIdArgument);
metadata.AddArgument(compensationIdArgument);
}
protected override void Execute(NativeActivityContext context)
{
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
long compensationId = this.compensationId.Get(context);
Fx.Assert(compensationId != CompensationToken.RootCompensationId, "CompensationId passed to the SecondaryRoot must be valid");
CompensationTokenData compensationToken = compensationExtension.Get(compensationId);
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
CompensationToken token = new CompensationToken(compensationToken);
this.currentCompensationToken.Set(context, token);
compensationToken.IsTokenValidInSecondaryRoot = true;
context.Properties.Add(CompensationToken.PropertyName, token);
Fx.Assert(compensationToken.BookmarkTable[CompensationBookmarkName.OnConfirmation] == null, "Bookmark should not be already initialized in the bookmark table.");
compensationToken.BookmarkTable[CompensationBookmarkName.OnConfirmation] = context.CreateBookmark(new BookmarkCallback(OnConfirmation));
Fx.Assert(compensationToken.BookmarkTable[CompensationBookmarkName.OnCompensation] == null, "Bookmark should not be already initialized in the bookmark table.");
compensationToken.BookmarkTable[CompensationBookmarkName.OnCompensation] = context.CreateBookmark(new BookmarkCallback(OnCompensation));
Fx.Assert(compensationToken.BookmarkTable[CompensationBookmarkName.OnCancellation] == null, "Bookmark should not be already initialized in the bookmark table.");
compensationToken.BookmarkTable[CompensationBookmarkName.OnCancellation] = context.CreateBookmark(new BookmarkCallback(OnCancellation));
Bookmark onSecondaryRootScheduled = compensationToken.BookmarkTable[CompensationBookmarkName.OnSecondaryRootScheduled];
Fx.Assert(onSecondaryRootScheduled != null, "onSecondaryRootScheduled bookmark must be already registered.");
compensationToken.BookmarkTable[CompensationBookmarkName.OnSecondaryRootScheduled] = null;
context.ResumeBookmark(onSecondaryRootScheduled, compensationId);
}
void OnConfirmation(NativeActivityContext context, Bookmark bookmark, object value)
{
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
long compensationId = (long)value;
Fx.Assert(compensationId != CompensationToken.RootCompensationId, "CompensationId must be passed when resuming the Completed bookmark");
CompensationTokenData compensationToken = compensationExtension.Get(compensationId);
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
Fx.Assert(compensationToken.CompensationState == CompensationState.Confirming, "CompensationState should be in Confirming state");
if (TD.CompensationStateIsEnabled())
{
TD.CompensationState(compensationToken.DisplayName, compensationToken.CompensationState.ToString());
}
compensationToken.RemoveBookmark(context, CompensationBookmarkName.OnCancellation);
compensationToken.RemoveBookmark(context, CompensationBookmarkName.OnCompensation);
if (ConfirmationHandler != null)
{
context.ScheduleActivity(ConfirmationHandler, new CompletionCallback(OnConfirmationHandlerComplete), new FaultCallback(OnExceptionFromHandler));
}
else
{
this.currentCompensationToken.Set(context, new CompensationToken(compensationToken));
if (compensationToken.ExecutionTracker.Count > 0)
{
context.ScheduleActivity(DefaultConfirmation, new CompletionCallback(this.OnConfirmationComplete));
}
else
{
compensationExtension.NotifyMessage(context, compensationToken.CompensationId, CompensationBookmarkName.Confirmed);
}
}
}
void OnConfirmationHandlerComplete(NativeActivityContext context, ActivityInstance completedInstance)
{
Fx.Assert(context != null, "context must be valid");
Fx.Assert(completedInstance != null, "completedInstance must be valid");
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
CompensationTokenData compensationToken = compensationExtension.Get(this.compensationId.Get(context));
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
if (completedInstance.State == ActivityInstanceState.Closed)
{
Fx.Assert(compensationToken.CompensationState == CompensationState.Confirming, "CompensationParticipant should be in Confirming State");
this.currentCompensationToken.Set(context, new CompensationToken(compensationToken));
if (compensationToken.ExecutionTracker.Count > 0)
{
context.ScheduleActivity(DefaultConfirmation, new CompletionCallback(this.OnConfirmationComplete));
}
else
{
compensationExtension.NotifyMessage(context, compensationToken.CompensationId, CompensationBookmarkName.Confirmed);
}
}
}
void OnConfirmationComplete(NativeActivityContext context, ActivityInstance completedInstance)
{
Fx.Assert(context != null, "context must be valid");
Fx.Assert(completedInstance != null, "completedInstance must be valid");
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
CompensationTokenData compensationToken = compensationExtension.Get(this.compensationId.Get(context));
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
if (completedInstance.State == ActivityInstanceState.Closed)
{
compensationExtension.NotifyMessage(context, compensationToken.CompensationId, CompensationBookmarkName.Confirmed);
}
}
void OnCompensation(NativeActivityContext context, Bookmark bookmark, object value)
{
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
long compensationId = (long)value;
Fx.Assert(compensationId != CompensationToken.RootCompensationId, "CompensationId must be passed when resuming the Completed bookmark");
CompensationTokenData compensationToken = compensationExtension.Get(compensationId);
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
Fx.Assert(compensationToken.CompensationState == CompensationState.Compensating, "CompensationState should be in Compensating state");
if (TD.CompensationStateIsEnabled())
{
TD.CompensationState(compensationToken.DisplayName, compensationToken.CompensationState.ToString());
}
// Cleanup Bookmarks..
compensationToken.RemoveBookmark(context, CompensationBookmarkName.OnCancellation);
compensationToken.RemoveBookmark(context, CompensationBookmarkName.OnConfirmation);
if (CompensationHandler != null)
{
context.ScheduleActivity(CompensationHandler, new CompletionCallback(this.OnCompensationHandlerComplete), new FaultCallback(OnExceptionFromHandler));
}
else
{
this.currentCompensationToken.Set(context, new CompensationToken(compensationToken));
if (compensationToken.ExecutionTracker.Count > 0)
{
context.ScheduleActivity(DefaultCompensation, new CompletionCallback(this.OnCompensationComplete));
}
else
{
this.InternalOnCompensationComplete(context, compensationExtension, compensationToken);
}
}
}
void OnCompensationHandlerComplete(NativeActivityContext context, ActivityInstance completedInstance)
{
Fx.Assert(context != null, "context must be valid");
Fx.Assert(completedInstance != null, "completedInstance must be valid");
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
CompensationTokenData compensationToken = compensationExtension.Get(this.compensationId.Get(context));
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
if (completedInstance.State == ActivityInstanceState.Closed)
{
Fx.Assert(compensationToken.CompensationState == CompensationState.Compensating, "CompensationParticipant should be in Compensating State");
this.currentCompensationToken.Set(context, new CompensationToken(compensationToken));
if (compensationToken.ExecutionTracker.Count > 0)
{
context.ScheduleActivity(DefaultConfirmation, new CompletionCallback(this.OnCompensationComplete));
}
else
{
this.InternalOnCompensationComplete(context, compensationExtension, compensationToken);
}
}
}
void OnCancellation(NativeActivityContext context, Bookmark bookmark, object value)
{
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
long compensationId = (long)value;
Fx.Assert(compensationId != CompensationToken.RootCompensationId, "CompensationId must be passed when resuming the Completed bookmark");
CompensationTokenData compensationToken = compensationExtension.Get(compensationId);
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
Fx.Assert(compensationToken.CompensationState == CompensationState.Canceling, "CompensationState should be in Canceling state");
if (TD.CompensationStateIsEnabled())
{
TD.CompensationState(compensationToken.DisplayName, compensationToken.CompensationState.ToString());
}
// remove bookmarks.
compensationToken.RemoveBookmark(context, CompensationBookmarkName.OnCompensation);
compensationToken.RemoveBookmark(context, CompensationBookmarkName.OnConfirmation);
this.currentCompensationToken.Set(context, new CompensationToken(compensationToken));
if (CancellationHandler != null)
{
context.ScheduleActivity(CancellationHandler, new CompletionCallback(this.OnCancellationHandlerComplete), new FaultCallback(OnExceptionFromHandler));
}
else
{
if (compensationToken.ExecutionTracker.Count > 0)
{
context.ScheduleActivity(DefaultCompensation, new CompletionCallback(this.OnCompensationComplete));
}
else
{
this.InternalOnCompensationComplete(context, compensationExtension, compensationToken);
}
}
}
void OnCancellationHandlerComplete(NativeActivityContext context, ActivityInstance completedInstance)
{
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
CompensationTokenData compensationToken = compensationExtension.Get(this.compensationId.Get(context));
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
if (completedInstance.State == ActivityInstanceState.Closed)
{
Fx.Assert(compensationToken.CompensationState == CompensationState.Canceling, "CompensationParticipant should be in Canceling State");
this.currentCompensationToken.Set(context, new CompensationToken(compensationToken));
if (compensationToken.ExecutionTracker.Count > 0)
{
context.ScheduleActivity(DefaultConfirmation, new CompletionCallback(this.OnCompensationComplete));
}
else
{
this.InternalOnCompensationComplete(context, compensationExtension, compensationToken);
}
}
}
void OnCompensationComplete(NativeActivityContext context, ActivityInstance completedInstance)
{
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
CompensationTokenData compensationToken = compensationExtension.Get(this.compensationId.Get(context));
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
InternalOnCompensationComplete(context, compensationExtension, compensationToken);
}
void InternalOnCompensationComplete(NativeActivityContext context, CompensationExtension compensationExtension, CompensationTokenData compensationToken)
{
switch (compensationToken.CompensationState)
{
case CompensationState.Canceling:
compensationExtension.NotifyMessage(context, compensationToken.CompensationId, CompensationBookmarkName.Canceled);
break;
case CompensationState.Compensating:
compensationExtension.NotifyMessage(context, compensationToken.CompensationId, CompensationBookmarkName.Compensated);
break;
default:
Fx.Assert(false, "CompensationState is in unexpected state!");
break;
}
}
void OnExceptionFromHandler(NativeActivityFaultContext context, Exception propagatedException, ActivityInstance propagatedFrom)
{
CompensationExtension compensationExtension = context.GetExtension<CompensationExtension>();
Fx.Assert(compensationExtension != null, "CompensationExtension must be valid");
CompensationTokenData compensationToken = compensationExtension.Get(this.compensationId.Get(context));
Fx.Assert(compensationToken != null, "CompensationTokenData must be valid");
InvalidOperationException exception = null;
switch (compensationToken.CompensationState)
{
case CompensationState.Confirming:
exception = new InvalidOperationException(SR.ConfirmationHandlerFatalException(compensationToken.DisplayName), propagatedException);
break;
case CompensationState.Compensating:
exception = new InvalidOperationException(SR.CompensationHandlerFatalException(compensationToken.DisplayName), propagatedException);
break;
case CompensationState.Canceling:
exception = new InvalidOperationException(SR.CancellationHandlerFatalException(compensationToken.DisplayName), propagatedException);
break;
default:
Fx.Assert(false, "CompensationState is in unexpected state!");
break;
}
context.Abort(exception);
}
}
}
|