|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Hosting
{
using System.Collections.Generic;
using System.Linq;
using System.Runtime;
using System.Activities.Tracking;
// One workflow host should have one manager, and one manager should have one catalog.
// One workflow instance should have one container as the instance itself would be
// added as one extension to the container as well
public class WorkflowInstanceExtensionManager
{
// using an empty list instead of null simplifies our calculations immensely
internal static List<KeyValuePair<Type, WorkflowInstanceExtensionProvider>> EmptyExtensionProviders = new List<KeyValuePair<Type, WorkflowInstanceExtensionProvider>>(0);
internal static List<object> EmptySingletonExtensions = new List<object>(0);
bool isReadonly;
List<object> additionalSingletonExtensions;
List<object> allSingletonExtensions;
bool hasSingletonTrackingParticipant;
bool hasSingletonPersistenceModule;
public WorkflowInstanceExtensionManager()
{
}
internal SymbolResolver SymbolResolver
{
get;
private set;
}
internal List<object> SingletonExtensions
{
get;
private set;
}
internal List<object> AdditionalSingletonExtensions
{
get
{
return this.additionalSingletonExtensions;
}
}
internal List<KeyValuePair<Type, WorkflowInstanceExtensionProvider>> ExtensionProviders
{
get;
private set;
}
internal bool HasSingletonIWorkflowInstanceExtensions
{
get;
private set;
}
internal bool HasSingletonTrackingParticipant
{
get
{
return this.hasSingletonTrackingParticipant;
}
}
internal bool HasSingletonPersistenceModule
{
get
{
return this.hasSingletonPersistenceModule;
}
}
internal bool HasAdditionalSingletonIWorkflowInstanceExtensions
{
get;
private set;
}
// use this method to add the singleton extension
public virtual void Add(object singletonExtension)
{
if (singletonExtension == null)
{
throw FxTrace.Exception.ArgumentNull("singletonExtension");
}
ThrowIfReadOnly();
if (singletonExtension is SymbolResolver)
{
if (this.SymbolResolver != null)
{
throw FxTrace.Exception.Argument("singletonExtension", SR.SymbolResolverAlreadyExists);
}
this.SymbolResolver = (SymbolResolver)singletonExtension;
}
else
{
if (singletonExtension is IWorkflowInstanceExtension)
{
HasSingletonIWorkflowInstanceExtensions = true;
}
if (!this.HasSingletonTrackingParticipant && singletonExtension is TrackingParticipant)
{
this.hasSingletonTrackingParticipant = true;
}
if (!this.HasSingletonPersistenceModule && singletonExtension is IPersistencePipelineModule)
{
this.hasSingletonPersistenceModule = true;
}
}
if (this.SingletonExtensions == null)
{
this.SingletonExtensions = new List<object>();
}
this.SingletonExtensions.Add(singletonExtension);
}
// use this method to add a per-instance extension
public virtual void Add<T>(Func<T> extensionCreationFunction) where T : class
{
if (extensionCreationFunction == null)
{
throw FxTrace.Exception.ArgumentNull("extensionCreationFunction");
}
ThrowIfReadOnly();
if (this.ExtensionProviders == null)
{
this.ExtensionProviders = new List<KeyValuePair<Type, WorkflowInstanceExtensionProvider>>();
}
this.ExtensionProviders.Add(new KeyValuePair<Type, WorkflowInstanceExtensionProvider>(typeof(T), new WorkflowInstanceExtensionProvider<T>(extensionCreationFunction)));
}
internal List<object> GetAllSingletonExtensions()
{
return this.allSingletonExtensions;
}
internal void AddAllExtensionTypes(HashSet<Type> extensionTypes)
{
Fx.Assert(this.isReadonly, "should be read only at this point");
for (int i = 0; i < this.SingletonExtensions.Count; i++)
{
extensionTypes.Add(this.SingletonExtensions[i].GetType());
}
for (int i = 0; i < this.ExtensionProviders.Count; i++)
{
extensionTypes.Add(this.ExtensionProviders[i].Key);
}
}
internal static WorkflowInstanceExtensionCollection CreateInstanceExtensions(Activity workflowDefinition, WorkflowInstanceExtensionManager extensionManager)
{
Fx.Assert(workflowDefinition.IsRuntimeReady, "activity should be ready with extensions after a successful CacheMetadata call");
if (extensionManager != null)
{
extensionManager.MakeReadOnly();
return new WorkflowInstanceExtensionCollection(workflowDefinition, extensionManager);
}
else if ((workflowDefinition.DefaultExtensionsCount > 0) || (workflowDefinition.RequiredExtensionTypesCount > 0))
{
return new WorkflowInstanceExtensionCollection(workflowDefinition, null);
}
else
{
return null;
}
}
internal static void AddExtensionClosure(object newExtension, ref List<object> targetCollection, ref bool addedTrackingParticipant, ref bool addedPersistenceModule)
{
// see if we need to process "additional" extensions
IWorkflowInstanceExtension currentInstanceExtension = newExtension as IWorkflowInstanceExtension;
if (currentInstanceExtension == null)
{
return; // bail early
}
Queue<IWorkflowInstanceExtension> additionalInstanceExtensions = null;
if (targetCollection == null)
{
targetCollection = new List<object>();
}
while (currentInstanceExtension != null)
{
IEnumerable<object> additionalExtensions = currentInstanceExtension.GetAdditionalExtensions();
if (additionalExtensions != null)
{
foreach (object additionalExtension in additionalExtensions)
{
targetCollection.Add(additionalExtension);
if (additionalExtension is IWorkflowInstanceExtension)
{
if (additionalInstanceExtensions == null)
{
additionalInstanceExtensions = new Queue<IWorkflowInstanceExtension>();
}
additionalInstanceExtensions.Enqueue((IWorkflowInstanceExtension)additionalExtension);
}
if (!addedTrackingParticipant && additionalExtension is TrackingParticipant)
{
addedTrackingParticipant = true;
}
if (!addedPersistenceModule && additionalExtension is IPersistencePipelineModule)
{
addedPersistenceModule = true;
}
}
}
if (additionalInstanceExtensions != null && additionalInstanceExtensions.Count > 0)
{
currentInstanceExtension = additionalInstanceExtensions.Dequeue();
}
else
{
currentInstanceExtension = null;
}
}
}
public void MakeReadOnly()
{
// if any singleton extensions have dependents, calculate them now so that we're only
// doing this process once per-host
if (!this.isReadonly)
{
if (this.SingletonExtensions != null)
{
if (HasSingletonIWorkflowInstanceExtensions)
{
foreach (IWorkflowInstanceExtension additionalExtensionProvider in this.SingletonExtensions.OfType<IWorkflowInstanceExtension>())
{
AddExtensionClosure(additionalExtensionProvider, ref this.additionalSingletonExtensions, ref this.hasSingletonTrackingParticipant, ref this.hasSingletonPersistenceModule);
}
if (this.AdditionalSingletonExtensions != null)
{
for (int i = 0; i < this.AdditionalSingletonExtensions.Count; i++)
{
object extension = this.AdditionalSingletonExtensions[i];
if (extension is IWorkflowInstanceExtension)
{
HasAdditionalSingletonIWorkflowInstanceExtensions = true;
break;
}
}
}
}
this.allSingletonExtensions = this.SingletonExtensions;
if (this.AdditionalSingletonExtensions != null && this.AdditionalSingletonExtensions.Count > 0)
{
this.allSingletonExtensions = new List<object>(this.SingletonExtensions);
this.allSingletonExtensions.AddRange(this.AdditionalSingletonExtensions);
}
}
else
{
this.SingletonExtensions = EmptySingletonExtensions;
this.allSingletonExtensions = EmptySingletonExtensions;
}
if (this.ExtensionProviders == null)
{
this.ExtensionProviders = EmptyExtensionProviders;
}
this.isReadonly = true;
}
}
void ThrowIfReadOnly()
{
if (this.isReadonly)
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ExtensionsCannotBeModified));
}
}
}
}
|