1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 
5 namespace System.ServiceModel.Activities
6 {
7     using System.Activities;
8     using System.Activities.Hosting;
9     using System.Collections.Generic;
10     using System.Collections.ObjectModel;
11     using System.Configuration;
12     using System.Diagnostics.CodeAnalysis;
13     using System.Globalization;
14     using System.IO;
15     using System.Runtime;
16     using System.Runtime.DurableInstancing;
17     using System.ServiceModel.Activation;
18     using System.ServiceModel.Activities.Configuration;
19     using System.ServiceModel.Activities.Description;
20     using System.ServiceModel.Activities.Dispatcher;
21     using System.ServiceModel.Channels;
22     using System.ServiceModel.Description;
23     using System.ServiceModel.Activities.Diagnostics;
24     using System.Xml;
25     using System.Xml.Linq;
26 
27     [Fx.Tag.XamlVisible(false)]
28     public class WorkflowServiceHost : ServiceHostBase
29     {
30         static readonly XName mexContractXName = XName.Get(ServiceMetadataBehavior.MexContractName, ServiceMetadataBehavior.MexContractNamespace);
31         static readonly Type mexBehaviorType = typeof(ServiceMetadataBehavior);
32         static readonly TimeSpan defaultPersistTimeout = TimeSpan.FromSeconds(30);
33         static readonly TimeSpan defaultTrackTimeout = TimeSpan.FromSeconds(30);
34         static readonly Type baseActivityType = typeof(Activity);
35         static readonly Type correlationQueryBehaviorType = typeof(CorrelationQueryBehavior);
36         static readonly Type bufferedReceiveServiceBehaviorType = typeof(BufferedReceiveServiceBehavior);
37 
38         WorkflowServiceHostExtensions workflowExtensions;
39         DurableInstanceManager durableInstanceManager;
40 
41         WorkflowDefinitionProvider workflowDefinitionProvider;
42 
43         Activity activity;
44         WorkflowService serviceDefinition;
45         IDictionary<XName, ContractDescription> inferredContracts;
46         IDictionary<XName, Collection<CorrelationQuery>> correlationQueries;
47 
48         WorkflowUnhandledExceptionAction unhandledExceptionAction;
49         TimeSpan idleTimeToPersist;
50         TimeSpan idleTimeToUnload;
51 
52         WorkflowServiceHostPerformanceCounters workflowServiceHostPerformanceCounters;
53 
54         [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors,
55             Justification = "Based on prior are from WCF3: By design, don't want to complicate ServiceHost state model")]
WorkflowServiceHost(object serviceImplementation, params Uri[] baseAddresses)56         public WorkflowServiceHost(object serviceImplementation, params Uri[] baseAddresses)
57             : base()
58         {
59             if (serviceImplementation == null)
60             {
61                 throw FxTrace.Exception.ArgumentNull("serviceImplementation");
62             }
63 
64             if (serviceImplementation is WorkflowService)
65             {
66                 InitializeFromConstructor((WorkflowService)serviceImplementation, baseAddresses);
67             }
68             else
69             {
70                 Activity activity = serviceImplementation as Activity;
71                 if (activity == null)
72                 {
73                     throw FxTrace.Exception.Argument("serviceImplementation", SR.InvalidServiceImplementation);
74                 }
75                 InitializeFromConstructor(activity, baseAddresses);
76             }
77         }
78 
79 
80         [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors,
81             Justification = "Based on prior are from WCF3: By design, don't want to complicate ServiceHost state model")]
WorkflowServiceHost(Activity activity, params Uri[] baseAddresses)82         public WorkflowServiceHost(Activity activity, params Uri[] baseAddresses)
83             : base()
84         {
85             if (activity == null)
86             {
87                 throw FxTrace.Exception.ArgumentNull("activity");
88             }
89 
90             InitializeFromConstructor(activity, baseAddresses);
91         }
92 
93         [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors,
94             Justification = "Based on prior art from WCF 3.0: By design, don't want to complicate ServiceHost state model")]
WorkflowServiceHost(WorkflowService serviceDefinition, params Uri[] baseAddresses)95         public WorkflowServiceHost(WorkflowService serviceDefinition, params Uri[] baseAddresses)
96             : base()
97         {
98             if (serviceDefinition == null)
99             {
100                 throw FxTrace.Exception.ArgumentNull("serviceDefinition");
101             }
102 
103             InitializeFromConstructor(serviceDefinition, baseAddresses);
104 
105         }
106 
107         [SuppressMessage(FxCop.Category.Usage, FxCop.Rule.DoNotCallOverridableMethodsInConstructors,
108             Justification = "Based on prior art from WCF 3.0: By design, don't want to complicate ServiceHost state model")]
WorkflowServiceHost()109         protected WorkflowServiceHost()
110         {
111             InitializeFromConstructor((WorkflowService)null);
112         }
113 
114         public Activity Activity
115         {
116             get
117             {
118                 return this.activity;
119             }
120         }
121 
122         public WorkflowInstanceExtensionManager WorkflowExtensions
123         {
124             get
125             {
126                 return this.workflowExtensions;
127             }
128         }
129 
130         public DurableInstancingOptions DurableInstancingOptions
131         {
132             get
133             {
134                 return this.durableInstanceManager.DurableInstancingOptions;
135             }
136         }
137 
138         public ICollection<WorkflowService> SupportedVersions
139         {
140             get
141             {
142                 return this.workflowDefinitionProvider.SupportedVersions;
143             }
144         }
145 
146         internal XName ServiceName
147         {
148             get;
149             set;
150         }
151 
152         internal TimeSpan PersistTimeout
153         {
154             get;
155             set;
156         }
157 
158         internal TimeSpan TrackTimeout
159         {
160             get;
161             set;
162         }
163 
164         //
165         internal TimeSpan FilterResumeTimeout
166         {
167             get;
168             set;
169         }
170 
171         internal DurableInstanceManager DurableInstanceManager
172         {
173             get
174             {
175                 return this.durableInstanceManager;
176             }
177         }
178 
179         internal bool IsLoadTransactionRequired
180         {
181             get;
182             private set;
183         }
184 
185         // set by WorkflowUnhandledExceptionBehavior.ApplyDispatchBehavior, used by WorkflowServiceInstance.UnhandledExceptionPolicy
186         internal WorkflowUnhandledExceptionAction UnhandledExceptionAction
187         {
188             get { return this.unhandledExceptionAction; }
189             set
190             {
191                 Fx.Assert(WorkflowUnhandledExceptionActionHelper.IsDefined(value), "Undefined WorkflowUnhandledExceptionAction");
192                 this.unhandledExceptionAction = value;
193             }
194         }
195 
196         // set by WorkflowIdleBehavior.ApplyDispatchBehavior, used by WorkflowServiceInstance.UnloadInstancePolicy
197         internal TimeSpan IdleTimeToPersist
198         {
199             get { return this.idleTimeToPersist; }
200             set
201             {
202                 Fx.Assert(value >= TimeSpan.Zero, "IdleTimeToPersist cannot be less than zero");
203                 this.idleTimeToPersist = value;
204             }
205         }
206         internal TimeSpan IdleTimeToUnload
207         {
208             get { return this.idleTimeToUnload; }
209             set
210             {
211                 Fx.Assert(value >= TimeSpan.Zero, "IdleTimeToUnload cannot be less than zero");
212                 this.idleTimeToUnload = value;
213             }
214         }
215 
216         internal bool IsConfigurable
217         {
218             get
219             {
220                 lock (this.ThisLock)
221                 {
222                     return this.State == CommunicationState.Created || this.State == CommunicationState.Opening;
223                 }
224             }
225         }
226 
227         internal WorkflowServiceHostPerformanceCounters WorkflowServiceHostPerformanceCounters
228         {
229             get
230             {
231                 return this.workflowServiceHostPerformanceCounters;
232             }
233         }
234 
235         internal bool OverrideSiteName
236         {
237             get;
238             set;
239         }
240 
InitializeFromConstructor(Activity activity, params Uri[] baseAddresses)241         void InitializeFromConstructor(Activity activity, params Uri[] baseAddresses)
242         {
243             WorkflowService serviceDefinition = new WorkflowService
244             {
245                 Body = activity
246             };
247 
248             InitializeFromConstructor(serviceDefinition, baseAddresses);
249         }
250 
InitializeFromConstructor(WorkflowService serviceDefinition, params Uri[] baseAddresses)251         void InitializeFromConstructor(WorkflowService serviceDefinition, params Uri[] baseAddresses)
252         {
253             // first initialize some values to their defaults
254             this.idleTimeToPersist = WorkflowIdleBehavior.defaultTimeToPersist;
255             this.idleTimeToUnload = WorkflowIdleBehavior.defaultTimeToUnload;
256             this.unhandledExceptionAction = WorkflowUnhandledExceptionBehavior.defaultAction;
257             this.workflowExtensions = new WorkflowServiceHostExtensions();
258 
259             // If the AppSettings.DefaultAutomaticInstanceKeyDisassociation is specified and is true, create a DisassociateInstanceKeysExtension, set its
260             // AutomaticDisassociationEnabled property to true, and add it to the extensions collection so that System.Activities.BookmarkScopeHandle will
261             // unregister its BookmarkScope, which will cause key disassociation. KB2669774.
262             if (AppSettings.DefaultAutomaticInstanceKeyDisassociation)
263             {
264                 DisassociateInstanceKeysExtension extension = new DisassociateInstanceKeysExtension();
265                 extension.AutomaticDisassociationEnabled = true;
266                 this.workflowExtensions.Add(extension);
267             }
268 
269             if (TD.CreateWorkflowServiceHostStartIsEnabled())
270             {
271                 TD.CreateWorkflowServiceHostStart();
272             }
273             if (serviceDefinition != null)
274             {
275                 this.workflowDefinitionProvider = new WorkflowDefinitionProvider(serviceDefinition, this);
276                 InitializeDescription(serviceDefinition, new UriSchemeKeyedCollection(baseAddresses));
277             }
278             this.durableInstanceManager = new DurableInstanceManager(this);
279 
280             if (TD.CreateWorkflowServiceHostStopIsEnabled())
281             {
282                 TD.CreateWorkflowServiceHostStop();
283             }
284 
285             this.workflowServiceHostPerformanceCounters = new WorkflowServiceHostPerformanceCounters(this);
286         }
287 
288         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DefaultParametersShouldNotBeUsed, Justification = "Temporary suppression - to be addressed by DCR 127467")]
AddServiceEndpoint(XName serviceContractName, Binding binding, string address, Uri listenUri = null, string behaviorConfigurationName = null)289         public ServiceEndpoint AddServiceEndpoint(XName serviceContractName, Binding binding, string address,
290             Uri listenUri = null, string behaviorConfigurationName = null)
291         {
292             return AddServiceEndpoint(serviceContractName, binding, new Uri(address, UriKind.RelativeOrAbsolute), listenUri, behaviorConfigurationName);
293         }
294 
295         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DefaultParametersShouldNotBeUsed, Justification = "Temporary suppression - to be addressed by DCR 127467")]
AddServiceEndpoint(XName serviceContractName, Binding binding, Uri address, Uri listenUri = null, string behaviorConfigurationName = null)296         public ServiceEndpoint AddServiceEndpoint(XName serviceContractName, Binding binding, Uri address,
297             Uri listenUri = null, string behaviorConfigurationName = null)
298         {
299             if (binding == null)
300             {
301                 throw FxTrace.Exception.ArgumentNull("binding");
302             }
303             if (address == null)
304             {
305                 throw FxTrace.Exception.ArgumentNull("address");
306             }
307 
308             Uri via = this.MakeAbsoluteUri(address, binding);
309             return AddServiceEndpointCore(serviceContractName, binding, new EndpointAddress(via), listenUri, behaviorConfigurationName);
310         }
311 
312         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.DefaultParametersShouldNotBeUsed, Justification = "Temporary suppression - to be addressed by DCR 127467")]
AddServiceEndpointCore(XName serviceContractName, Binding binding, EndpointAddress address, Uri listenUri = null, string behaviorConfigurationName = null)313         ServiceEndpoint AddServiceEndpointCore(XName serviceContractName, Binding binding, EndpointAddress address,
314             Uri listenUri = null, string behaviorConfigurationName = null)
315         {
316             if (serviceContractName == null)
317             {
318                 throw FxTrace.Exception.ArgumentNull("serviceContractName");
319             }
320             if (this.inferredContracts == null)
321             {
322                 throw FxTrace.Exception.AsError(new InvalidOperationException(
323                     SR.ContractNotFoundInAddServiceEndpoint(serviceContractName.LocalName, serviceContractName.NamespaceName)));
324             }
325 
326             ServiceEndpoint serviceEndpoint;
327             ContractDescription description;
328 
329             ContractInferenceHelper.ProvideDefaultNamespace(ref serviceContractName);
330 
331             if (this.inferredContracts.TryGetValue(serviceContractName, out description))
332             {
333                 serviceEndpoint = new ServiceEndpoint(description, binding, address);
334 
335                 if (!string.IsNullOrEmpty(behaviorConfigurationName))
336                 {
337                     ConfigLoader.LoadChannelBehaviors(behaviorConfigurationName, null, serviceEndpoint.Behaviors);
338                 }
339             }
340             else if (serviceContractName == mexContractXName)  // Special case for mex endpoint
341             {
342                 if (!this.Description.Behaviors.Contains(mexBehaviorType))
343                 {
344                     throw FxTrace.Exception.AsError(new InvalidOperationException(
345                         SR.ServiceMetadataBehaviorNotFoundForServiceMetadataEndpoint(this.Description.Name)));
346                 }
347 
348                 serviceEndpoint = new ServiceMetadataEndpoint(binding, address);
349             }
350             else
351             {
352                 throw FxTrace.Exception.AsError(new InvalidOperationException(
353                     SR.ContractNotFoundInAddServiceEndpoint(serviceContractName.LocalName, serviceContractName.NamespaceName)));
354             }
355 
356             if (listenUri != null)
357             {
358                 listenUri = base.MakeAbsoluteUri(listenUri, binding);
359                 serviceEndpoint.ListenUri = listenUri;
360             }
361 
362             base.Description.Endpoints.Add(serviceEndpoint);
363 
364             if (TD.ServiceEndpointAddedIsEnabled())
365             {
366                 TD.ServiceEndpointAdded(address.Uri.ToString(), binding.GetType().ToString(), serviceEndpoint.Contract.Name);
367             }
368 
369             return serviceEndpoint;
370         }
371 
372         // Duplicate public AddServiceEndpoint methods from the base class
373         // This is to ensure that base class methods with string are not hidden by derived class methods with XName
AddServiceEndpoint(string implementedContract, Binding binding, string address)374         public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address)
375         {
376             return base.AddServiceEndpoint(implementedContract, binding, address);
377         }
378 
AddServiceEndpoint(string implementedContract, Binding binding, Uri address)379         public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address)
380         {
381             return base.AddServiceEndpoint(implementedContract, binding, address);
382         }
383 
AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri)384         public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri)
385         {
386             return base.AddServiceEndpoint(implementedContract, binding, address, listenUri);
387         }
388 
AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri)389         public new ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri)
390         {
391             return base.AddServiceEndpoint(implementedContract, binding, address, listenUri);
392         }
393 
AddServiceEndpoint(ServiceEndpoint endpoint)394         public override void AddServiceEndpoint(ServiceEndpoint endpoint)
395         {
396             if (!endpoint.IsSystemEndpoint)
397             {
398                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CannotUseAddServiceEndpointOverloadForWorkflowServices));
399             }
400 
401             base.AddServiceEndpoint(endpoint);
402         }
403 
AddDefaultEndpoints(Binding defaultBinding, List<ServiceEndpoint> defaultEndpoints)404         internal override void AddDefaultEndpoints(Binding defaultBinding, List<ServiceEndpoint> defaultEndpoints)
405         {
406             if (this.inferredContracts != null)
407             {
408                 foreach (XName contractName in this.inferredContracts.Keys)
409                 {
410                     ServiceEndpoint endpoint = AddServiceEndpoint(contractName, defaultBinding, String.Empty);
411                     ConfigLoader.LoadDefaultEndpointBehaviors(endpoint);
412                     AddCorrelationQueryBehaviorToServiceEndpoint(endpoint);
413                     defaultEndpoints.Add(endpoint);
414                 }
415             }
416         }
417 
418         [SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters, MessageId = "0#", Justification = "This is defined by the ServiceHost base class")]
CreateDescription(out IDictionary<string, ContractDescription> implementedContracts)419         protected override ServiceDescription CreateDescription(out IDictionary<string, ContractDescription> implementedContracts)
420         {
421             Fx.AssertAndThrow(this.serviceDefinition != null, "serviceDefinition is null");
422 
423             this.activity = this.serviceDefinition.Body;
424 
425             Dictionary<string, ContractDescription> result = new Dictionary<string, ContractDescription>();
426 
427             // Note: We do not check whether this.inferredContracts == null || this.inferredContracts.Count == 0,
428             // because we need to support hosting workflow with zero contract.
429             this.inferredContracts = this.serviceDefinition.GetContractDescriptions();
430 
431             if (this.inferredContracts != null)
432             {
433                 foreach (ContractDescription contract in this.inferredContracts.Values)
434                 {
435                     if (!string.IsNullOrEmpty(contract.ConfigurationName))
436                     {
437                         if (result.ContainsKey(contract.ConfigurationName))
438                         {
439                             throw FxTrace.Exception.AsError(new InvalidOperationException(SR.DifferentContractsSameConfigName));
440                         }
441                         result.Add(contract.ConfigurationName, contract);
442                     }
443                 }
444             }
445 
446             implementedContracts = result;
447 
448             // Currently, only WorkflowService has CorrelationQueries property
449             this.correlationQueries = this.serviceDefinition.CorrelationQueries;
450             ServiceDescription serviceDescription = this.serviceDefinition.GetEmptyServiceDescription();
451             serviceDescription.Behaviors.Add(new WorkflowServiceBehavior(this.workflowDefinitionProvider));
452             return serviceDescription;
453         }
454 
InitializeDescription(WorkflowService serviceDefinition, UriSchemeKeyedCollection baseAddresses)455         void InitializeDescription(WorkflowService serviceDefinition, UriSchemeKeyedCollection baseAddresses)
456         {
457             Fx.Assert(serviceDefinition != null, "caller must verify");
458 
459             this.serviceDefinition = serviceDefinition;
460             base.InitializeDescription(baseAddresses);
461 
462             foreach (Endpoint endpoint in serviceDefinition.Endpoints)
463             {
464                 if (endpoint.Binding == null)
465                 {
466                     string endpointName = ContractValidationHelper.GetErrorMessageEndpointName(endpoint.Name);
467                     string contractName = ContractValidationHelper.GetErrorMessageEndpointServiceContractName(endpoint.ServiceContractName);
468                     throw FxTrace.Exception.AsError(new InvalidOperationException(SR.MissingBindingInEndpoint(endpointName, contractName)));
469                 }
470 
471                 ServiceEndpoint serviceEndpoint = AddServiceEndpointCore(endpoint.ServiceContractName, endpoint.Binding,
472                         endpoint.GetAddress(this), endpoint.ListenUri, endpoint.BehaviorConfigurationName);
473 
474                 if (!string.IsNullOrEmpty(endpoint.Name))
475                 {
476                     serviceEndpoint.Name = endpoint.Name;
477                 }
478                 serviceEndpoint.UnresolvedAddress = endpoint.AddressUri;
479                 serviceEndpoint.UnresolvedListenUri = endpoint.ListenUri;
480             }
481 
482             this.PersistTimeout = defaultPersistTimeout;
483             this.TrackTimeout = defaultTrackTimeout;
484             this.FilterResumeTimeout = TimeSpan.FromSeconds(AppSettings.FilterResumeTimeoutInSeconds);
485         }
486 
InitializeRuntime()487         protected override void InitializeRuntime()
488         {
489             if (base.Description != null)
490             {
491                 FixupEndpoints();
492                 this.SetScopeName();
493                 if (this.DurableInstancingOptions.ScopeName == null)
494                 {
495                     this.DurableInstancingOptions.ScopeName = XNamespace.Get(this.Description.Namespace).GetName(this.Description.Name);
496                 }
497             }
498 
499             base.InitializeRuntime();
500 
501             this.WorkflowServiceHostPerformanceCounters.InitializePerformanceCounters();
502 
503             this.ServiceName = XNamespace.Get(this.Description.Namespace).GetName(this.Description.Name);
504 
505             // add a host-wide SendChannelCache (with default settings) if one doesn't exist
506             this.workflowExtensions.EnsureChannelCache();
507 
508             // add a host-wide (free-threaded) CorrelationExtension based on our ServiceName
509             this.WorkflowExtensions.Add(new CorrelationExtension(this.DurableInstancingOptions.ScopeName));
510 
511             this.WorkflowExtensions.MakeReadOnly();
512 
513             // now calculate if IsLoadTransactionRequired
514             this.IsLoadTransactionRequired = WorkflowServiceInstance.IsLoadTransactionRequired(this);
515 
516             if (this.serviceDefinition != null)
517             {
518                 ValidateBufferedReceiveProperty();
519                 this.serviceDefinition.ResetServiceDescription();
520             }
521         }
522 
AfterInitializeRuntime(TimeSpan timeout)523         internal override void AfterInitializeRuntime(TimeSpan timeout)
524         {
525             this.durableInstanceManager.Open(timeout);
526         }
527 
BeginAfterInitializeRuntime(TimeSpan timeout, AsyncCallback callback, object state)528         internal override IAsyncResult BeginAfterInitializeRuntime(TimeSpan timeout, AsyncCallback callback, object state)
529         {
530             return this.durableInstanceManager.BeginOpen(timeout, callback, state);
531         }
532 
EndAfterInitializeRuntime(IAsyncResult result)533         internal override void EndAfterInitializeRuntime(IAsyncResult result)
534         {
535             this.durableInstanceManager.EndOpen(result);
536         }
537 
OnClose(TimeSpan timeout)538         protected override void OnClose(TimeSpan timeout)
539         {
540             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
541 
542             base.OnClose(timeoutHelper.RemainingTime());
543 
544             this.durableInstanceManager.Close(timeoutHelper.RemainingTime());
545 
546             this.workflowServiceHostPerformanceCounters.Dispose();
547         }
548 
OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)549         protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
550         {
551             return new CloseAsyncResult(this, timeout, callback, state);
552         }
553 
OnEndClose(IAsyncResult result)554         protected override void OnEndClose(IAsyncResult result)
555         {
556             CloseAsyncResult.End(result);
557         }
558 
OnAbort()559         protected override void OnAbort()
560         {
561             base.OnAbort();
562 
563             this.durableInstanceManager.Abort();
564 
565             this.workflowServiceHostPerformanceCounters.Dispose();
566         }
567 
FaultServiceHostIfNecessary(Exception exception)568         internal void FaultServiceHostIfNecessary(Exception exception)
569         {
570             if (exception is InstancePersistenceException && !(exception is InstancePersistenceCommandException))
571             {
572                 this.Fault(exception);
573             }
574         }
575 
BeginHostClose(TimeSpan timeout, AsyncCallback callback, object state)576         IAsyncResult BeginHostClose(TimeSpan timeout, AsyncCallback callback, object state)
577         {
578             return base.OnBeginClose(timeout, callback, state);
579         }
EndHostClose(IAsyncResult result)580         void EndHostClose(IAsyncResult result)
581         {
582             base.OnEndClose(result);
583         }
584 
AddCorrelationQueryBehaviorToServiceEndpoint(ServiceEndpoint serviceEndpoint)585         void AddCorrelationQueryBehaviorToServiceEndpoint(ServiceEndpoint serviceEndpoint)
586         {
587             Fx.Assert(serviceEndpoint != null, "Argument cannot be null!");
588             Fx.Assert(serviceEndpoint.Contract != null, "ServiceEndpoint must have a contract!");
589             Fx.Assert(this.serviceDefinition != null, "Missing WorkflowService!");
590             Fx.Assert(!serviceEndpoint.Behaviors.Contains(correlationQueryBehaviorType),
591                 "ServiceEndpoint should not have CorrelationQueryBehavior before this point!");
592 
593             XName endpointContractName = XName.Get(serviceEndpoint.Contract.Name, serviceEndpoint.Contract.Namespace);
594 
595             Collection<CorrelationQuery> queries;
596             if (this.correlationQueries != null && this.correlationQueries.TryGetValue(endpointContractName, out queries))
597             {
598                 // Filter out duplicate CorrelationQueries in the collection.
599                 // Currently, we only do reference comparison and Where message filter comparison.
600                 Collection<CorrelationQuery> uniqueQueries = new Collection<CorrelationQuery>();
601                 foreach (CorrelationQuery correlationQuery in queries)
602                 {
603                     if (!uniqueQueries.Contains(correlationQuery))
604                     {
605                         uniqueQueries.Add(correlationQuery);
606                     }
607                     else
608                     {
609                         if (TD.DuplicateCorrelationQueryIsEnabled())
610                         {
611                             TD.DuplicateCorrelationQuery(correlationQuery.Where.ToString());
612                         }
613                     }
614                 }
615                 serviceEndpoint.Behaviors.Add(new CorrelationQueryBehavior(uniqueQueries) { ServiceContractName = endpointContractName });
616             }
617             else if (CorrelationQueryBehavior.BindingHasDefaultQueries(serviceEndpoint.Binding))
618             {
619                 if (!serviceEndpoint.Behaviors.Contains(typeof(CorrelationQueryBehavior)))
620                 {
621                     serviceEndpoint.Behaviors.Add(new CorrelationQueryBehavior(new Collection<CorrelationQuery>()) { ServiceContractName = endpointContractName });
622                 }
623             }
624         }
625 
FixupEndpoints()626         void FixupEndpoints()
627         {
628             Fx.Assert(this.Description != null, "ServiceDescription cannot be null");
629 
630             Dictionary<Type, ContractDescription> contractDescriptionDictionary = new Dictionary<Type, ContractDescription>();
631             foreach (ServiceEndpoint serviceEndpoint in this.Description.Endpoints)
632             {
633                 if (this.serviceDefinition.AllowBufferedReceive)
634                 {
635                     // All application-level endpoints need to support ReceiveContext
636                     SetupReceiveContextEnabledAttribute(serviceEndpoint);
637                 }
638 
639                 // Need to add CorrelationQueryBehavior here so that endpoints added from config are included.
640                 // It is possible that some endpoints already have CorrelationQueryBehavior from
641                 // the AddDefaultEndpoints code path. We should skip them.
642                 if (!serviceEndpoint.Behaviors.Contains(correlationQueryBehaviorType))
643                 {
644                     AddCorrelationQueryBehaviorToServiceEndpoint(serviceEndpoint);
645                 }
646 
647                 // Need to ensure that any WorkflowHostingEndpoints using the same contract type actually use the
648                 // same contractDescription instance since this is required by WCF.
649                 if (serviceEndpoint is WorkflowHostingEndpoint)
650                 {
651                     ContractDescription contract;
652                     if (contractDescriptionDictionary.TryGetValue(serviceEndpoint.Contract.ContractType, out contract))
653                     {
654                         serviceEndpoint.Contract = contract;
655                     }
656                     else
657                     {
658                         contractDescriptionDictionary[serviceEndpoint.Contract.ContractType] = serviceEndpoint.Contract;
659                     }
660                 }
661             }
662 
663             if (this.serviceDefinition.AllowBufferedReceive && !this.Description.Behaviors.Contains(bufferedReceiveServiceBehaviorType))
664             {
665                 this.Description.Behaviors.Add(new BufferedReceiveServiceBehavior());
666             }
667         }
668 
SetScopeName()669         void SetScopeName()
670         {
671             VirtualPathExtension virtualPathExtension = this.Extensions.Find<VirtualPathExtension>();
672             if (virtualPathExtension != null)
673             {
674                 // Web Hosted scenario
675                 WorkflowHostingOptionsSection hostingOptions = (WorkflowHostingOptionsSection)ConfigurationManager.GetSection(ConfigurationStrings.WorkflowHostingOptionsSectionPath);
676                 if (hostingOptions != null && hostingOptions.OverrideSiteName)
677                 {
678                     this.OverrideSiteName = hostingOptions.OverrideSiteName;
679 
680                     string fullVirtualPath = virtualPathExtension.VirtualPath.Substring(1);
681                     fullVirtualPath = ("/" == virtualPathExtension.ApplicationVirtualPath) ? fullVirtualPath : virtualPathExtension.ApplicationVirtualPath + fullVirtualPath;
682 
683                     int index = fullVirtualPath.LastIndexOf("/", StringComparison.OrdinalIgnoreCase);
684                     string virtualDirectoryPath = fullVirtualPath.Substring(0, index + 1);
685 
686                     this.DurableInstancingOptions.ScopeName = XName.Get(XmlConvert.EncodeLocalName(Path.GetFileName(virtualPathExtension.VirtualPath)),
687                         string.Format(CultureInfo.InvariantCulture, "/{0}{1}", this.Description.Name, virtualDirectoryPath));
688                 }
689             }
690         }
691 
SetupReceiveContextEnabledAttribute(ServiceEndpoint serviceEndpoint)692         void SetupReceiveContextEnabledAttribute(ServiceEndpoint serviceEndpoint)
693         {
694             if (BufferedReceiveServiceBehavior.IsWorkflowEndpoint(serviceEndpoint))
695             {
696                 foreach (OperationDescription operation in serviceEndpoint.Contract.Operations)
697                 {
698                     ReceiveContextEnabledAttribute behavior = operation.Behaviors.Find<ReceiveContextEnabledAttribute>();
699                     if (behavior == null)
700                     {
701                         operation.Behaviors.Add(new ReceiveContextEnabledAttribute() { ManualControl = true });
702                     }
703                     else
704                     {
705                         behavior.ManualControl = true;
706                     }
707                 }
708             }
709         }
710 
ValidateBufferedReceiveProperty()711         void ValidateBufferedReceiveProperty()
712         {
713             // Validate that the AttachedProperty is indeed being used when the behavior is also used
714             bool hasBehavior = this.Description.Behaviors.Contains(bufferedReceiveServiceBehaviorType);
715             if (hasBehavior && !this.serviceDefinition.AllowBufferedReceive)
716             {
717                 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.BufferedReceiveBehaviorUsedWithoutProperty));
718             }
719         }
720 
721         // specialized WorkflowInstanceExtensionManager that can default in a SendMessageChannelCache
722         class WorkflowServiceHostExtensions : WorkflowInstanceExtensionManager
723         {
724             static Type SendReceiveExtensionType = typeof(SendReceiveExtension);
725 
726             bool hasChannelCache;
727 
WorkflowServiceHostExtensions()728             public WorkflowServiceHostExtensions()
729                 : base()
730             {
731             }
732 
Add(Func<T> extensionCreationFunction)733             public override void Add<T>(Func<T> extensionCreationFunction)
734             {
735                 ThrowIfNotSupported(typeof(T));
736 
737                 if (TypeHelper.AreTypesCompatible(typeof(T), typeof(SendMessageChannelCache)))
738                 {
739                     this.hasChannelCache = true;
740                 }
741                 base.Add<T>(extensionCreationFunction);
742             }
743 
Add(object singletonExtension)744             public override void Add(object singletonExtension)
745             {
746                 ThrowIfNotSupported(singletonExtension.GetType());
747 
748                 if (singletonExtension is SendMessageChannelCache)
749                 {
750                     this.hasChannelCache = true;
751                 }
752                 base.Add(singletonExtension);
753             }
754 
EnsureChannelCache()755             public void EnsureChannelCache()
756             {
757                 if (!this.hasChannelCache)
758                 {
759                     Add(new SendMessageChannelCache());
760                     this.hasChannelCache = true;
761                 }
762             }
763 
ThrowIfNotSupported(Type type)764             void ThrowIfNotSupported(Type type)
765             {
766                 if (TypeHelper.AreTypesCompatible(type, SendReceiveExtensionType))
767                 {
768                     throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ExtensionTypeNotSupported(SendReceiveExtensionType.FullName)));
769                 }
770             }
771         }
772 
773         class CloseAsyncResult : AsyncResult
774         {
775             static AsyncCompletion handleDurableInstanceManagerEndClose = new AsyncCompletion(HandleDurableInstanceManagerEndClose);
776             static AsyncCompletion handleEndHostClose = new AsyncCompletion(HandleEndHostClose);
777 
778             TimeoutHelper timeoutHelper;
779             WorkflowServiceHost host;
780 
CloseAsyncResult(WorkflowServiceHost host, TimeSpan timeout, AsyncCallback callback, object state)781             public CloseAsyncResult(WorkflowServiceHost host, TimeSpan timeout, AsyncCallback callback, object state)
782                 : base(callback, state)
783             {
784                 this.timeoutHelper = new TimeoutHelper(timeout);
785                 this.host = host;
786 
787                 if (CloseHost())
788                 {
789                     Complete(true);
790                 }
791             }
792 
CloseDurableInstanceManager()793             bool CloseDurableInstanceManager()
794             {
795                 IAsyncResult result = this.host.durableInstanceManager.BeginClose(
796                     this.timeoutHelper.RemainingTime(), base.PrepareAsyncCompletion(handleDurableInstanceManagerEndClose), this);
797                 return SyncContinue(result);
798             }
799 
CloseHost()800             bool CloseHost()
801             {
802                 IAsyncResult result = this.host.BeginHostClose(
803                     this.timeoutHelper.RemainingTime(), base.PrepareAsyncCompletion(handleEndHostClose), this);
804                 return SyncContinue(result);
805             }
806 
HandleDurableInstanceManagerEndClose(IAsyncResult result)807             static bool HandleDurableInstanceManagerEndClose(IAsyncResult result)
808             {
809                 CloseAsyncResult thisPtr = (CloseAsyncResult)result.AsyncState;
810 
811                 thisPtr.host.durableInstanceManager.EndClose(result);
812 
813                 thisPtr.host.WorkflowServiceHostPerformanceCounters.Dispose();
814                 return true;
815             }
816 
HandleEndHostClose(IAsyncResult result)817             static bool HandleEndHostClose(IAsyncResult result)
818             {
819                 CloseAsyncResult thisPtr = (CloseAsyncResult)result.AsyncState;
820 
821                 thisPtr.host.EndHostClose(result);
822                 return thisPtr.CloseDurableInstanceManager();
823             }
824 
End(IAsyncResult result)825             public static void End(IAsyncResult result)
826             {
827                 AsyncResult.End<CloseAsyncResult>(result);
828             }
829         }
830     }
831 }
832