Enlistment.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / TransactionBridge / Microsoft / Transactions / Wsat / Protocol / Enlistment.cs / 1 / Enlistment.cs

                            //------------------------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------

// This file contains classes representing various types of enlistments 

using System; 
using System.ServiceModel.Channels; 
using System.Collections;
using System.Collections.Generic; 
using System.ServiceModel;
using System.ServiceModel.Transactions;
using System.Diagnostics;
using System.ServiceModel.Security; 
using System.Threading;
using Microsoft.Transactions.Bridge; 
using Microsoft.Transactions.Wsat.InputOutput; 
using Microsoft.Transactions.Wsat.Messaging;
using Microsoft.Transactions.Wsat.StateMachines; 

using Activity = System.ServiceModel.Diagnostics.Activity;
using DiagnosticUtility = Microsoft.Transactions.Bridge.DiagnosticUtility;
 
namespace Microsoft.Transactions.Wsat.Protocol
{ 
    // Base class for all enlistment objects 
    abstract class TransactionEnlistment
    { 
        protected ProtocolState state;
        protected StateMachine stateMachine;
        protected TransactionContextManager ourContextManager;
 
        protected Enlistment enlistment;
        protected Guid enlistmentId; 
 
        protected ProtocolProviderCallback lastCallback;
        protected object lastCallbackState; 

        int retries;
        bool removeEnlistmentFromLookupTable;
 
        protected TransactionEnlistment(ProtocolState state)
            : 
            this(state, Guid.NewGuid()) { } 

        protected TransactionEnlistment(ProtocolState state, Guid enlistmentId) 
        {
            this.state = state;
            this.enlistmentId = enlistmentId;
        } 

        public override string ToString() 
        { 
            return this.enlistmentId.ToString();
        } 

        public ProtocolState State
        {
            get { return this.state; } 
        }
 
        public TransactionContextManager ContextManager 
        {
            get { return this.ourContextManager; } 
            set { this.ourContextManager = value; }
        }

        public Enlistment Enlistment 
        {
            get { return this.enlistment; } 
        } 

        public Guid EnlistmentId 
        {
            get { return this.enlistmentId; }
        }
 
        public StateMachine StateMachine
        { 
            get { return this.stateMachine; } 
        }
 
        public int Retries
        {
            get { return this.retries; }
            set { this.retries = value; } 
        }
 
        public void SetCallback(ProtocolProviderCallback callback, object callbackState) 
        {
            this.lastCallback = callback; 
            this.lastCallbackState = callbackState;
        }

        public void DeliverCallback(Status status) 
        {
            this.lastCallback(this.enlistment, status, this.lastCallbackState); 
            this.lastCallback = null; 
            this.lastCallbackState = null;
        } 

        // Calculate a timespan derived from the input (TimeSpan.Zero means invalid timeout)
        TimeSpan CalculateTimeout(uint expires, bool expiresPresent)
        { 
            if (!expiresPresent)
            { 
                // No value was provided.  Use the default 
                return state.Config.DefaultTimeout;
            } 

            if (expires == 0)
            {
                // An explicit zero value was provided. Enforce the maximum timeout. 
                // A value of zero means no timeout for both the MaxTimeout property and the TM
                return state.Config.MaxTimeout; 
            } 

            TimeSpan timeout = TimeoutHelper.FromMilliseconds(expires); 

            if (state.Config.MaxTimeout != TimeSpan.Zero &&
                state.Config.MaxTimeout < timeout)
            { 
                timeout = state.Config.MaxTimeout;
            } 
 
            return timeout;
        } 

        public EnlistmentOptions CreateEnlistmentOptions(uint expires,
                                                         bool expiresPresent,
                                                         System.Transactions.IsolationLevel isoLevel, 
                                                         IsolationFlags isoFlags,
                                                         string description) 
        { 
            EnlistmentOptions options = new EnlistmentOptions();
 
            // Expires
            options.Expires = CalculateTimeout(expires, expiresPresent);

            // IsoLevel 
            options.IsoLevel = isoLevel;
            if (options.IsoLevel == System.Transactions.IsolationLevel.Unspecified) 
            { 
                options.IsoLevel = System.Transactions.IsolationLevel.Serializable;
            } 

            // IsoFlags
            options.IsolationFlags = isoFlags;
 
            // Description
            options.Description = description; 
 
            return options;
        } 

        protected void VerifyAndTraceEnlistmentOptions()
        {
            EnlistmentOptions options = this.enlistment.EnlistmentOptions; 

            // By the time that TransactionEnlistment.SetTransactionContext is called, 
            // the EnlistmentOptions property must be properly setup. The 
            // TransactionEnlistment type is internal to the implementation.
            // Therefore, the assumptions are things that only a Microsoft dev could 
            // violate. Violations of these assumptions is a bug that needs to be
            // reported back to Microsoft immediately.
            if (options == null)
            { 
                DiagnosticUtility.FailFast("Need EnlistmentOptions for context");
            } 
            if (options.IsoLevel == System.Transactions.IsolationLevel.Unspecified) 
            {
                DiagnosticUtility.FailFast("Need IsolationLevel for context"); 
            }
            if (this.enlistment.LocalTransactionId == Guid.Empty)
            {
                DiagnosticUtility.FailFast("Need LocalTransactionId for context"); 
            }
            if (string.IsNullOrEmpty(this.enlistment.RemoteTransactionId)) 
            { 
                DiagnosticUtility.FailFast("Need RemoteTransactionId for context");
            } 

            if (DebugTrace.Info)
            {
                DebugTrace.TxTrace(TraceLevel.Info, this.enlistmentId, "Local transactionId is {0}", this.enlistment.LocalTransactionId); 
                DebugTrace.TxTrace(TraceLevel.Info, this.enlistmentId, "Remote transactionId is {0}", this.enlistment.RemoteTransactionId);
                DebugTrace.TxTrace(TraceLevel.Info, this.enlistmentId, "Transaction timeout is {0} seconds", options.Expires.TotalSeconds); 
                DebugTrace.TxTrace(TraceLevel.Info, this.enlistmentId, "Transaction isolation level is {0}", options.IsoLevel); 
            }
        } 

        protected void TraceTransferEvent()
        {
            // Associate the enlistment identifier with the transaction identifier 
            // We could do this elsewhere, but this is a good place to do it
            if (Microsoft.Transactions.Bridge.DiagnosticUtility.ShouldUseActivity) 
            { 
                using (Activity.CreateActivity(this.enlistment.LocalTransactionId))
                { 
                    Microsoft.Transactions.Bridge.DiagnosticUtility.DiagnosticTrace.TraceTransfer(this.enlistmentId);
                }
            }
        } 

        protected void FindAndActivateTransactionContextManager() 
        { 
            // Find the context manager - there must be one in the table when this is called
            string identifier = this.enlistment.RemoteTransactionId; 
            TransactionContextManager contextManager = state.Lookup.FindTransactionContextManager(identifier);
            Debug.Assert(contextManager != null, "Must have a contextManager in FindAndActivateTransactionContextManager");

            ActivateTransactionContextManager(contextManager); 
        }
 
        protected void ActivateTransactionContextManager(TransactionContextManager contextManager) 
        {
            TransactionContext context = CreateTransactionContext(); 
            contextManager.StateMachine.Enqueue(new TransactionContextCreatedEvent(contextManager, context));
        }

        TransactionContext CreateTransactionContext() 
        {
            EnlistmentOptions options = this.enlistment.EnlistmentOptions; 
            string contextId = this.enlistment.RemoteTransactionId; 
            Guid transactionId = this.enlistment.LocalTransactionId;
 
            CoordinationContext context = new CoordinationContext(this.state.ProtocolVersion);
            RequestSecurityTokenResponse issuedToken = null;

            context.Expires = (uint) TimeoutHelper.ToMilliseconds(options.Expires); 
            context.Identifier = contextId;
            context.LocalTransactionId = transactionId; 
            context.IsolationLevel = options.IsoLevel; 
            context.IsolationFlags = options.IsolationFlags;
            context.Description = options.Description; 

            string identifier = CoordinationContext.IsNativeIdentifier(contextId, transactionId) ?
                                                                       null : contextId;
            string tokenId = null; 
            if (state.Config.PortConfiguration.SupportingTokensEnabled)
            { 
                CoordinationServiceSecurity.CreateIssuedToken(transactionId, 
                                                              contextId,
                                                              this.state.ProtocolVersion, 
                                                              out issuedToken,
                                                              out tokenId);
            }
 
            AddressHeader refParam = new WsatRegistrationHeader(transactionId, identifier, tokenId);
            context.RegistrationService = state.RegistrationCoordinatorListener.CreateEndpointReference(refParam); 
 
            return new TransactionContext(context, issuedToken);
        } 

        public virtual void OnStateMachineComplete()
        {
            if (this.removeEnlistmentFromLookupTable) 
            {
                state.Lookup.RemoveEnlistment(this); 
                this.removeEnlistmentFromLookupTable = false; 
            }
 
            if (this.ourContextManager != null)
            {
                this.ourContextManager.StateMachine.Enqueue(
                    new TransactionContextTransactionDoneEvent(this.ourContextManager)); 
            }
        } 
 
        protected void AddToLookupTable()
        { 
            state.Lookup.AddEnlistment(this);
            this.removeEnlistmentFromLookupTable = true;
        }
    } 

    abstract class CoordinatorEnlistmentBase : TransactionEnlistment 
    { 
        // This EPR represents our protocol service. It is used to send
        // register messages and as the 'from' field when sending protocol messages 
        protected EndpointAddress participantService;
        protected TwoPhaseCommitCoordinatorProxy coordinatorProxy;

        protected CoordinatorEnlistmentBase(ProtocolState state) 
            :
            base(state) { } 
 
        protected CoordinatorEnlistmentBase(ProtocolState state, Guid enlistmentId)
            : 
            base(state, enlistmentId) { }

        public TwoPhaseCommitCoordinatorProxy CoordinatorProxy
        { 
            get { return this.coordinatorProxy; }
        } 
 
        public EndpointAddress ParticipantService
        { 
            get { return this.participantService; }
        }

        public void SetCoordinatorProxy (TwoPhaseCommitCoordinatorProxy proxy) 
        {
            if (this.participantService == null) 
            { 
                // Documents the assumption that CreateParticipantService is always
                // called before SetCoordinatorProxy. If this assumption is violated, 
                // we have a product bug that needs to be reported back to Microsoft.
                DiagnosticUtility.FailFast("participantService needed for coordinatorProxy");
            }
 
            proxy.AddRef();
            this.coordinatorProxy = proxy; 
            this.coordinatorProxy.From = this.participantService; 
        }
 
        public override void OnStateMachineComplete()
        {
            base.OnStateMachineComplete();
 
            if (this.coordinatorProxy != null)
            { 
                this.coordinatorProxy.Release(); 
            }
        } 

        public void CreateParticipantService()
        {
            // Create a header representing the enlistment 
            EnlistmentHeader header = new EnlistmentHeader(this.enlistmentId);
            this.participantService = state.TwoPhaseCommitParticipantListener.CreateEndpointReference(header); 
 
            // In non-recovery scenarios, the coordinator proxy is set at RegisterResponse time,
            // so when CreateParticipantService is called this.coordinatorProxy will be null. 
            //
            // In recovery scenarios, we will have the coordinator proxy in hand in the constructor,
            // but we won't be able to create the participant service until later
            if (this.coordinatorProxy != null) 
            {
                this.coordinatorProxy.From = this.participantService; 
            } 
        }
    } 

    // Representation of a remote coordinator (superior) volatile enlistment
    class VolatileCoordinatorEnlistment : CoordinatorEnlistmentBase
    { 
        CoordinatorEnlistment coordinator;
 
        public VolatileCoordinatorEnlistment(ProtocolState state, CoordinatorEnlistment coordinator) 
            :
            base(state) 
        {
            this.coordinator = coordinator;

            // We use the same enlistment and state machine as the main coordinator 
            this.enlistment = this.coordinator.Enlistment;
            this.stateMachine = this.coordinator.StateMachine; 
 
            CreateParticipantService();
        } 

        public CoordinatorEnlistment Coordinator
        {
            get { return this.coordinator; } 
        }
    } 
 
    // Representation of a remote coordinator (superior) enlistment
    class CoordinatorEnlistment : CoordinatorEnlistmentBase 
    {
        // The volatile coordinator that is either registering or registered,
        VolatileCoordinatorEnlistment registerVolatileCoordinator;
 
        // The volatile coordinator for which we are currently processing a volatile Prepare
        VolatileCoordinatorEnlistment preparingVolatileCoordinator; 
 
        // The last volatile coordinator for which we processed a volatile Prepare
        VolatileCoordinatorEnlistment lastCompletedVolatileCoordinator; 

        // The recovered coordinator service
        // This is only set for recovered enlistments
        EndpointAddress recoveredCoordinatorService; 

        // We send register messages to this endpoint 
        CoordinationContext superiorContext; 
        RequestSecurityTokenResponse superiorRstr;
 
        RegistrationProxy registrationProxy;

        TmEnlistPrePrepareEvent enlistPrePrepareEvent;
 
        // The list of volatile coordinators we've created
        List volatileCoordinators; 
 
        // This constructor is called for new non-root transactions
        // Every such transaction is created thanks to a CCC w/ context message 
        // Therefore, we will always be entrusted with a TransactionContextManager
        // instance at this point in time.
        public CoordinatorEnlistment(ProtocolState state,
                                     TransactionContextManager contextManager, 
                                     CoordinationContext context,
                                     RequestSecurityTokenResponse rstr) 
            : 
            base(state)
        { 
            this.ourContextManager = contextManager;

            this.superiorContext = context;
            this.superiorRstr = rstr; 

            ConfigureEnlistment(context); 
 
            this.stateMachine = new CoordinatorStateMachine (this);
            this.stateMachine.ChangeState(state.States.CoordinatorInitializing); 
        }

        // This constructor is called during recovery
        public CoordinatorEnlistment (ProtocolState state, 
                                      Enlistment enlistment,
                                      Guid enlistmentId, 
                                      EndpointAddress service) 
                                      :
                                      base (state, enlistmentId) 
        {
            this.enlistment = enlistment;
            this.enlistment.ProtocolProviderContext = this;
 
            this.recoveredCoordinatorService = service;
            this.stateMachine = new CoordinatorStateMachine (this); 
 
            this.coordinatorProxy = state.TryCreateTwoPhaseCommitCoordinatorProxy (service);
            if (this.coordinatorProxy == null) 
            {
                // We have recovered an endpoint for which we cannot create a proxy
                // We can't just drop this enlistment, because we'd risk sending a polite message
                // under the wrong circumstances.  So we keep the enlistment around in a failed 
                // state, with a null proxy. Incoming messages will check for that null
                // proxy and send InvalidPolicy faults in response to any new messages. 
 
                if (RecoveredCoordinatorInvalidMetadataRecord.ShouldTrace)
                { 
                    RecoveredCoordinatorInvalidMetadataRecord.Trace(
                        this.enlistmentId,
                        enlistment.RemoteTransactionId,
                        service, 
                        this.state.ProtocolVersion
                        ); 
                } 

                this.stateMachine.ChangeState(state.States.CoordinatorFailedRecovery); 
            }
            else
            {
                this.stateMachine.ChangeState(state.States.CoordinatorRecovering); 
            }
 
            AddToLookupTable(); 
        }
 
        void ConfigureEnlistment(CoordinationContext context)
        {
            Enlistment enlistment = new Enlistment();
 
            string transactionId = context.Identifier;
            if (transactionId == null) 
            { 
                // The assumption in this code is that by the time the ConfigureEnlistment
                // method is called, the context in the WS-AT message was already validated 
                // and proper error handling already occurred in case things like the
                // TransactionId were not set. If this assumption is violated, we no longer
                // have a way to communicate to the user how to fix things-- somehow we got
                // to this place in the code and that just shouldn't happen. This is a bug 
                // that needs to be reported back to Microsoft.
                DiagnosticUtility.FailFast("Need transactionId to create enlistment"); 
            } 

            enlistment.RemoteTransactionId = transactionId; 
            enlistment.LocalTransactionId = Ports.GetGuidFromTransactionId(transactionId);
            enlistment.ProtocolProviderContext = this;

            this.enlistment = enlistment; 
        }
 
        // 
        // Properties
        // 

        public CoordinationContext SuperiorContext
        {
            get { return this.superiorContext; } 
        }
 
        public RequestSecurityTokenResponse SuperiorIssuedToken 
        {
            get { return this.superiorRstr; } 
        }

        public RegistrationProxy RegistrationProxy
        { 
            get { return this.registrationProxy; }
        } 
 
        public VolatileCoordinatorEnlistment RegisterVolatileCoordinator
        { 
            get { return this.registerVolatileCoordinator; }
            set { this.registerVolatileCoordinator = value; }
        }
 
        public VolatileCoordinatorEnlistment PreparingVolatileCoordinator
        { 
            get { return this.preparingVolatileCoordinator; } 
            set { this.preparingVolatileCoordinator = value; }
        } 

        public VolatileCoordinatorEnlistment LastCompletedVolatileCoordinator
        {
            get { return this.lastCompletedVolatileCoordinator; } 
            set { this.lastCompletedVolatileCoordinator = value; }
        } 
 
        public TmEnlistPrePrepareEvent EnlistPrePrepareEvent
        { 
            get { return this.enlistPrePrepareEvent; }
            set
            {
                if (this.enlistPrePrepareEvent != null && value != null) 
                {
                    // The event can only be set by internal code. The event should 
                    // not have a value and then be set by a caller to a new, non-null 
                    // value. Doing so indicates a product bug that must be reported back
                    // to Microsoft. 
                    DiagnosticUtility.FailFast("Cannot clobber EnlistPrePrepareEvent");
                }
                this.enlistPrePrepareEvent = value;
            } 
        }
 
        public void SetRegistrationProxy(RegistrationProxy proxy) 
        {
            proxy.AddRef(); 
            this.registrationProxy = proxy;
        }

        public void OnEnlistPrePrepare(TmEnlistPrePrepareEvent e) 
        {
            this.enlistPrePrepareEvent = e; 
 
            // Fault in the list.  We can do this safely because we're in the
            // state machine's synchronization domain 
            if (this.volatileCoordinators == null)
            {
                this.volatileCoordinators = new List();
            } 

            // Make the next volatile coordinator enlistment 
            VolatileCoordinatorEnlistment enlistment = new VolatileCoordinatorEnlistment(state, this); 
            this.volatileCoordinators.Add(enlistment);
            state.Lookup.AddEnlistment(enlistment); 

            if (this.registerVolatileCoordinator != null)
            {
                // If the VolatileCoordinatorEnlistment that is registering/registered is 
                // already set, then this method was called in error. Since the code path
                // is only accessible by internal code, calling the method twice indicates 
                // a product bug that must be reported back to Microsoft. 
                DiagnosticUtility.FailFast("Duplicate EnlistPrePrepare from TM");
            } 
            this.registerVolatileCoordinator = enlistment;
        }

        public void OnCoordinatorEnlisted() 
        {
            CreateParticipantService(); 
            AddToLookupTable(); 
            VerifyAndTraceEnlistmentOptions();
            TraceTransferEvent(); 
        }

        public void OnDurableCoordinatorActive()
        { 
            // It's very important that we do this here and no sooner
            // See MB 50939 for more details 
            FindAndActivateTransactionContextManager(); 
        }
 
        public override void OnStateMachineComplete()
        {
            base.OnStateMachineComplete();
 
            if (this.registrationProxy != null)
            { 
                this.registrationProxy.Release(); 
            }
 
            if (this.volatileCoordinators != null)
            {
                foreach (VolatileCoordinatorEnlistment volatileEnlistment in this.volatileCoordinators)
                { 
                    state.Lookup.RemoveEnlistment(volatileEnlistment);
                } 
            } 

            // We don't clean up any pending EnlistPrePrepare events because the TM doesn't care 
            // We might be sending too many callbacks in a row, which could confuse a PPL implementation
        }
    }
 
    class ParticipantEnlistment : TransactionEnlistment
    { 
        ControlProtocol protocol; 

        // This EPR is ours. It is used to send a register response message and as the 'from' 
        // when sending protocol messages
        EndpointAddress coordinatorService;
        TwoPhaseCommitParticipantProxy participantProxy;
 
        TimeSpan timeoutEstimate;
        long lastMessageTime; 
 
        // This constructor is called during recovery
        public ParticipantEnlistment(ProtocolState state, 
                                     Enlistment enlistment,
                                     Guid enlistmentId,
                                     EndpointAddress service)
            : 
            base(state, enlistmentId)
        { 
            this.enlistment = enlistment; 
            this.enlistment.ProtocolProviderContext = this;
            this.protocol = ControlProtocol.Durable2PC; 

            this.stateMachine = new DurableStateMachine (this);

            this.participantProxy = state.TryCreateTwoPhaseCommitParticipantProxy (service); 
            if (this.participantProxy == null)
            { 
                // This means that we are recovering an endpoint whose policy no longer intersects 
                // with our own.  This can happen.  For example, we were interoperating with some
                // other system, and then the administrator turned off our interop endpoint. 
                //
                // There is also the possibility for another MS endpoint to be configured in such a
                // way that while we can no longer talk to it, given the EPR it provided at registration time.
                // That endpoint might then be reconfigured to intersect with us again using a 
                // different policy choice.
                // 
                // We can't just drop this enlistment, because we'd risk sending a polite message 
                // under the wrong circumstances.  So we keep the enlistment around in a failed
                // state, with a null proxy. Incoming messages will check for that null 
                // proxy and send InvalidPolicy faults in response to any new messages.

                if (RecoveredParticipantInvalidMetadataRecord.ShouldTrace)
                { 
                    RecoveredParticipantInvalidMetadataRecord.Trace(
                        this.enlistmentId, 
                        enlistment.RemoteTransactionId, 
                        service,
                        this.state.ProtocolVersion 
                        );
                }

                this.stateMachine.ChangeState(state.States.DurableFailedRecovery); 
            }
            else 
            { 
                this.stateMachine.ChangeState(state.States.DurableRecovering);
            } 

            AddToLookupTable();
        }
 
        // This constructor is called for new participant enlistments
        public ParticipantEnlistment(ProtocolState state, 
                                     WsatRegistrationHeader header, 
                                     ControlProtocol protocol,
                                     TwoPhaseCommitParticipantProxy proxy) 
            :
            base(state)
        {
            this.protocol = protocol; 

            proxy.AddRef(); 
            this.participantProxy = proxy; 

            ConfigureEnlistment(header); 
            CreateCoordinatorService();

            switch (protocol)
            { 
                case ControlProtocol.Durable2PC:
                    this.stateMachine = new DurableStateMachine (this); 
                    this.stateMachine.ChangeState(state.States.DurableRegistering); 
                    break;
 
                case ControlProtocol.Volatile2PC:
                    this.stateMachine = new VolatileStateMachine (this);
                    this.stateMachine.ChangeState(state.States.VolatileRegistering);
                    break; 

                default: 
                    // An invalid Enum value on this internal code path indicates 
                    // a product bug and violates assumptions about
                    // valid values in MSDTC. 
                    DiagnosticUtility.FailFast("Invalid protocol");
                    break;  // Keep the compiler happy
            }
        } 

        // This constructor is called for new dummy subordinate enlistments 
        public ParticipantEnlistment(ProtocolState state, 
                                     Enlistment coordinatorEnlistment,
                                     TransactionContextManager contextManager) 
            :
            base(state)
        {
            this.protocol = ControlProtocol.Volatile2PC; 

            // Take ownership of the context manager 
            this.ourContextManager = contextManager; 

            // Create a 2PC enlistment so that when we transition to the SubordinateFinished state, 
            // it will be Phase 1 already: there won't be any more legitimate CCC w/ context messages
            this.enlistment = new Enlistment();
            this.enlistment.LocalTransactionId = coordinatorEnlistment.LocalTransactionId;
            this.enlistment.RemoteTransactionId = coordinatorEnlistment.RemoteTransactionId; 
            this.enlistment.NotificationMask = Notifications.TwoPhaseCommit | Notifications.Volatile;
            this.enlistment.ProtocolProviderContext = this; 
 
            this.stateMachine = new SubordinateStateMachine(this);
            this.stateMachine.ChangeState(state.States.SubordinateInitializing); 
        }

        public TimeSpan TimeoutEstimate
        { 
            get { return this.timeoutEstimate; }
        } 
 
        public override void OnStateMachineComplete()
        { 
            base.OnStateMachineComplete();

            if (this.participantProxy != null)
            { 
                this.participantProxy.Release();
            } 
        } 

        void ConfigureEnlistment(WsatRegistrationHeader header) 
        {
            Enlistment enlistment = new Enlistment();

            string contextId = header.ContextId; 
            if (contextId == null)
            { 
                contextId = CoordinationContext.CreateNativeIdentifier(header.TransactionId); 
            }
 
            enlistment.LocalTransactionId = header.TransactionId;
            enlistment.RemoteTransactionId = contextId;

            // Some volatile enlistments might not want outcome, but they'll only tell us that 
            // when we tell them to Prepare. Consequently, all participants must register
            // in advance for 2PC 
            Notifications mask = Notifications.TwoPhaseCommit; 
            switch (this.protocol)
            { 
                case ControlProtocol.Volatile2PC:

                    // Volatile registrants expect Phase Zero messages
                    // We also add the volatile flag which ensures no recovery 
                    mask |= Notifications.Phase0 | Notifications.Volatile;
                    break; 
 
                case ControlProtocol.Durable2PC:
                    break; 

                default:
                    // An invalid Enum value on this internal code path indicates
                    // a product bug and violates assumptions about 
                    // valid values in MSDTC.
                    DiagnosticUtility.FailFast("Invalid protocol"); 
                    break;  // Keep the compiler happy 
            }
 
            enlistment.NotificationMask = mask;
            enlistment.ProtocolProviderContext = this;

            this.enlistment = enlistment; 
        }
 
        public void CreateCoordinatorService() 
        {
            if ((this.protocol != ControlProtocol.Durable2PC) && 
                  (this.protocol != ControlProtocol.Volatile2PC))
            {
                // The CoordinatorService should only be created for
                // Durable|Volatile2PC. Anything else is a bug. 
                DiagnosticUtility.FailFast("Need protocol for coordinator service");
            } 
 
            EnlistmentHeader header = new EnlistmentHeader(this.enlistmentId, this.protocol);
            this.coordinatorService = state.TwoPhaseCommitCoordinatorListener.CreateEndpointReference (header); 
            this.participantProxy.From = this.coordinatorService;
        }

        public void OnParticipantRegistered() 
        {
            AddToLookupTable(); 
 
            VerifyAndTraceEnlistmentOptions();
            TraceTransferEvent(); 

            // Calculate the approximate time when the transaction will time out
            // This is used later by the preparing timer
            this.timeoutEstimate = state.ElapsedTime + this.enlistment.EnlistmentOptions.Expires; 
        }
 
        public void OnSubordinateRegistered() 
        {
            FindAndActivateTransactionContextManager(); 

            VerifyAndTraceEnlistmentOptions();
            TraceTransferEvent();
 
            // We don't need to add ourselves to the lookup table
        } 
 
        //
        // Properties 
        //

        public ControlProtocol ControlProtocol
        { 
            get { return this.protocol; }
        } 
 
        public long LastMessageTime
        { 
            get { return this.lastMessageTime; }
            set { this.lastMessageTime = value; }
        }
 
        public EndpointAddress CoordinatorService
        { 
            get { return this.coordinatorService; } 
        }
 
        public TwoPhaseCommitParticipantProxy ParticipantProxy
        {
            get { return this.participantProxy; }
        } 
    }
 
    class CompletionEnlistment : TransactionEnlistment 
    {
        // This EPR is ours. It is used to send a register response message and as the 'from' 
        // when sending protocol messages
        EndpointAddress coordinatorService;
        CompletionParticipantProxy participantProxy;
 
        //
        // Construction 
        // 

        public CompletionEnlistment(ProtocolState state) 
            :
            base(state, Guid.Empty)
        {
            ConfigureEnlistment(); 

            this.stateMachine = new CompletionStateMachine(this); 
            this.stateMachine.ChangeState(state.States.CompletionInitializing); 
        }
 
        public override void OnStateMachineComplete()
        {
            base.OnStateMachineComplete();
 
            if (this.participantProxy != null)
            { 
                this.participantProxy.Release(); 
            }
        } 

        void ConfigureEnlistment()
        {
            Enlistment enlistment = new Enlistment(); 
            enlistment.ProtocolProviderContext = this;
            this.enlistment = enlistment; 
        } 

        public EndpointAddress CoordinatorService 
        {
            get { return this.coordinatorService; }
        }
 
        public CompletionParticipantProxy ParticipantProxy
        { 
            get { return this.participantProxy; } 
        }
 
        public void SetCompletionProxy(CompletionParticipantProxy proxy)
        {
            proxy.AddRef();
            this.participantProxy = proxy; 
        }
 
        public void OnRootTransactionCreated() 
        {
            this.enlistmentId = this.enlistment.LocalTransactionId; 
            this.enlistment.RemoteTransactionId = CoordinationContext.CreateNativeIdentifier(this.enlistmentId);

            EnlistmentHeader header = new EnlistmentHeader(this.enlistmentId);
            this.coordinatorService = state.CompletionCoordinatorListener.CreateEndpointReference(header); 

            this.ourContextManager = new TransactionContextManager(state, this.enlistment.RemoteTransactionId); 
            state.Lookup.AddTransactionContextManager(this.ourContextManager); 
            ActivateTransactionContextManager(this.ourContextManager);
 
            AddToLookupTable();

            VerifyAndTraceEnlistmentOptions();
            TraceTransferEvent(); 
        }
    } 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK