File: System\ServiceModel\Activities\WorkflowControlEndpoint.cs
Project: ndp\cdf\src\NetFx40\System.ServiceModel.Activities\System.ServiceModel.Activities.csproj (System.ServiceModel.Activities)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
namespace System.ServiceModel.Activities
{
    using System.Diagnostics;
    using System.Globalization;
    using System.ServiceModel;
    using System.ServiceModel.Activities.Description;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.Runtime;
    using System.Threading;
    using System.Security;
    using System.Security.Permissions;
    
    [Fx.Tag.XamlVisible(false)]
    public class WorkflowControlEndpoint : ServiceEndpoint
    {
        static Uri defaultBaseUri;
 
        // Double-checked locking pattern requires volatile for read/write synchronization
        static volatile ContractDescription workflowControlServiceBaseContract;
        static volatile ContractDescription workflowControlServiceContract;
        static object workflowContractDescriptionLock = new object();
 
        public WorkflowControlEndpoint()
            : this(WorkflowControlEndpoint.GetDefaultBinding(),
            new EndpointAddress(new Uri(WorkflowControlEndpoint.DefaultBaseUri, new Uri(Guid.NewGuid().ToString(), UriKind.Relative))))
        {
        }
 
        public WorkflowControlEndpoint(Binding binding, EndpointAddress address)
            : base(WorkflowControlEndpoint.WorkflowControlServiceContract, binding, address)
        {
            this.IsSystemEndpoint = true;
        }
 
        internal static ContractDescription WorkflowControlServiceBaseContract
        {
            get
            {
                if (workflowControlServiceBaseContract == null)
                {
                    lock (workflowContractDescriptionLock)
                    {
                        if (workflowControlServiceBaseContract == null)
                        {
                            foreach (OperationDescription operation in WorkflowControlServiceContract.Operations)
                            {
                                if (operation.DeclaringContract != WorkflowControlServiceContract)
                                {
                                    workflowControlServiceBaseContract = operation.DeclaringContract;
                                    break;
                                }
                            }
                        }
                    }
                }
                return workflowControlServiceBaseContract;
            }
        }
 
        internal static ContractDescription WorkflowControlServiceContract
        {
            get
            {
                if (workflowControlServiceContract == null)
                {
                    lock (workflowContractDescriptionLock)
                    {
                        if (workflowControlServiceContract == null)
                        {
                            ContractDescription tempControlServiceContract = ContractDescription.GetContract(
                                typeof(IWorkflowUpdateableInstanceManagement));
                            tempControlServiceContract.Behaviors.Add(new ServiceMetadataContractBehavior(true));
                            ApplyOperationBehaviors(tempControlServiceContract);
                            // For back-compat, need to support existing code which expects the old contract type
                            tempControlServiceContract.ContractType = typeof(IWorkflowInstanceManagement);
                            workflowControlServiceContract = tempControlServiceContract;
                        }
                    }
                }
                return workflowControlServiceContract;
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Critical because it retreives the Process Id via Process.Id, which has a link demand for Full Trust.",
            Safe = "Safe because it demands FullTrust")]
        [SecuritySafeCritical]
        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
        static void RetrieveProcessIdAndAppDomainId(out int processId, out int appDomainId)
        {
            processId = Process.GetCurrentProcess().Id;
            appDomainId = AppDomain.CurrentDomain.Id;
        }
 
        static Uri DefaultBaseUri
        {
            get
            {
                if (defaultBaseUri == null)
                {
                    // If we are running in full trust, use the ProcessId and AppDomainId. If partial trust, use a new guid to make the URI unique and avoid
                    // the usage of ProcessId and AppDomainId. We are doing this for back compat.
                    if (PartialTrustHelpers.AppDomainFullyTrusted)
                    {
                        int processId;
                        int appDomainId;
                        RetrieveProcessIdAndAppDomainId(out processId, out appDomainId);
                        defaultBaseUri = new Uri(string.Format(CultureInfo.InvariantCulture, "net.pipe://localhost/workflowControlServiceEndpoint/{0}/{1}",
                            processId,
                            appDomainId));
                    }
                    else
                    {
                        Uri tempUri = new Uri(string.Format(CultureInfo.InvariantCulture, "net.pipe://localhost/workflowControlServiceEndpoint/{0}",
                            Guid.NewGuid().ToString()));
                        // Using Interlocked.CompareExchange because new Guid.NewGuid is not atomic.
                        Interlocked.CompareExchange(ref defaultBaseUri, tempUri, null);
                    }
                }
                return defaultBaseUri;
            }
        }
 
        static Binding GetDefaultBinding()
        {
            return new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) { TransactionFlow = true };
        }
 
        static void ApplyOperationBehaviors(ContractDescription contractDescription)
        {
            foreach (OperationDescription operationDescription in contractDescription.Operations)
            {
                //Except "Abandon" all the operations in this contract are Async.
                //All Transacted* operation are Transacted & Async.
                switch (operationDescription.Name)
                {
                    case XD2.WorkflowInstanceManagementService.Abandon:
                    case XD2.WorkflowInstanceManagementService.Cancel:
                    case XD2.WorkflowInstanceManagementService.Run:
                    case XD2.WorkflowInstanceManagementService.Suspend:
                    case XD2.WorkflowInstanceManagementService.Terminate:
                    case XD2.WorkflowInstanceManagementService.Unsuspend:
                    case XD2.WorkflowInstanceManagementService.Update:
                        EnsureDispatch(operationDescription);
                        break;
                    case XD2.WorkflowInstanceManagementService.TransactedCancel:
                    case XD2.WorkflowInstanceManagementService.TransactedRun:
                    case XD2.WorkflowInstanceManagementService.TransactedSuspend:
                    case XD2.WorkflowInstanceManagementService.TransactedTerminate:
                    case XD2.WorkflowInstanceManagementService.TransactedUnsuspend:
                    case XD2.WorkflowInstanceManagementService.TransactedUpdate:
                        EnsureDispatch(operationDescription);
                        EnsureTransactedInvoke(operationDescription);
                        break;
                }
            }
        }
 
        static void EnsureDispatch(OperationDescription operationDescription)
        {
            operationDescription.Behaviors.Add(new ControlOperationBehavior(false));
        }
 
        static void EnsureTransactedInvoke(OperationDescription operationDescription)
        {
            OperationBehaviorAttribute operationAttribute = operationDescription.Behaviors.Find<OperationBehaviorAttribute>();
            operationAttribute.TransactionScopeRequired = true;
        }
    }
}