4 using System
.ComponentModel
;
5 using System
.ComponentModel
.Design
;
6 using System
.ComponentModel
.Design
.Serialization
;
7 using System
.Diagnostics
;
8 using System
.Collections
;
9 using System
.Collections
.Generic
;
10 using System
.Collections
.Specialized
;
11 using System
.Collections
.ObjectModel
;
12 using System
.Configuration
;
13 using System
.Reflection
;
14 using System
.Threading
;
15 using System
.Globalization
;
19 using System
.Workflow
.Runtime
.Hosting
;
20 using System
.Workflow
.Runtime
.Configuration
;
21 using System
.Workflow
.ComponentModel
;
22 using System
.Workflow
.Runtime
.Tracking
;
23 using System
.Workflow
.ComponentModel
.Compiler
;
24 using System
.Workflow
.ComponentModel
.Serialization
;
25 using System
.Security
.Cryptography
;
26 using System
.Workflow
.ComponentModel
.Design
;
31 namespace System
.Workflow
.Runtime
33 internal sealed class WorkflowDefinitionDispenser
: IDisposable
35 private MruCache workflowTypes
;
36 private MruCache xomlFragments
;
37 private Dictionary
<Type
, List
<PropertyInfo
>> workflowOutParameters
;
38 private WorkflowRuntime workflowRuntime
;
39 private bool validateOnCreate
= true;
40 internal static DependencyProperty WorkflowDefinitionHashCodeProperty
= DependencyProperty
.RegisterAttached("WorkflowDefinitionHashCode", typeof(byte[]), typeof(WorkflowDefinitionDispenser
));
41 internal event EventHandler
<WorkflowDefinitionEventArgs
> WorkflowDefinitionLoaded
;
43 private ReaderWriterLock parametersLock
;
45 internal WorkflowDefinitionDispenser(WorkflowRuntime runtime
, bool validateOnCreate
, int capacity
)
51 this.workflowRuntime
= runtime
;
52 this.workflowTypes
= new MruCache(capacity
, this, CacheType
.Type
);
53 this.xomlFragments
= new MruCache(capacity
, this, CacheType
.Xoml
);
54 this.workflowOutParameters
= new Dictionary
<Type
, List
<PropertyInfo
>>();
55 this.parametersLock
= new ReaderWriterLock();
56 this.validateOnCreate
= validateOnCreate
;
59 internal ReadOnlyCollection
<PropertyInfo
> GetOutputParameters(Activity rootActivity
)
61 Type workflowType
= rootActivity
.GetType();
62 this.parametersLock
.AcquireReaderLock(-1);
65 if (this.workflowOutParameters
.ContainsKey(workflowType
))
66 return new ReadOnlyCollection
<PropertyInfo
>(this.workflowOutParameters
[workflowType
]);
70 this.parametersLock
.ReleaseLock();
73 // We will recurse at most once because CacheOutputParameters() will perform negative caching.
74 CacheOutputParameters(rootActivity
);
75 return GetOutputParameters(rootActivity
);
78 internal void GetWorkflowTypes(out ReadOnlyCollection
<Type
> keys
, out ReadOnlyCollection
<Activity
> values
)
80 this.workflowTypes
.GetWorkflowDefinitions
<Type
>(out keys
, out values
);
83 internal void GetWorkflowDefinitions(out ReadOnlyCollection
<byte[]> keys
, out ReadOnlyCollection
<Activity
> values
)
85 this.xomlFragments
.GetWorkflowDefinitions
<byte[]>(out keys
, out values
);
88 internal Activity
GetWorkflowDefinition(byte[] xomlHashCode
)
90 Activity workflowDefinition
= null;
91 if (xomlHashCode
== null)
92 throw new ArgumentNullException("xomlHashCode");
94 workflowDefinition
= xomlFragments
.GetDefinition(xomlHashCode
);
96 if (workflowDefinition
== null)
97 throw new ArgumentException("xomlHashCode");
99 return workflowDefinition
;
102 internal Activity
GetWorkflowDefinition(Type workflowType
)
104 if (workflowType
== null)
105 throw new ArgumentNullException("workflowType");
107 return this.GetRootActivity(workflowType
, false, true);
110 internal Activity
GetRootActivity(Type workflowType
, bool createNew
, bool initForRuntime
)
112 Activity root
= null;
115 return LoadRootActivity(workflowType
, false, initForRuntime
);
117 root
= workflowTypes
.GetOrGenerateDefinition(workflowType
, null, null, null, initForRuntime
, out exist
);
122 // Set the locking object used for cloning the definition
123 // for non-internal use (WorkflowInstance.GetWorkflowDefinition
124 // and WorkflowCompletedEventArgs.WorkflowDefinition)
125 WorkflowDefinitionLock
.SetWorkflowDefinitionLockObject(root
, new object());
127 EventHandler
<WorkflowDefinitionEventArgs
> localWorkflowDefinitionLoaded
= WorkflowDefinitionLoaded
;
128 if (localWorkflowDefinitionLoaded
!= null)
129 localWorkflowDefinitionLoaded(this.workflowRuntime
, new WorkflowDefinitionEventArgs(workflowType
));
134 // This function will create a new root activity definition tree by deserializing the xoml and the rules file.
135 // The last parameter createNew should be true when the caller is asking for a new definition for performing
136 // dynamic updates instead of a cached definition.
137 internal Activity
GetRootActivity(string xomlText
, string rulesText
, bool createNew
, bool initForRuntime
)
139 if (string.IsNullOrEmpty(xomlText
))
140 throw new ArgumentNullException("xomlText");
142 //calculate the "hash". Think 60s!
143 byte[] xomlHashCode
= null;
144 MemoryStream xomlBytesStream
= new MemoryStream();
145 using (StreamWriter streamWriter
= new StreamWriter(xomlBytesStream
))
147 streamWriter
.Write(xomlText
);
149 //consider rules, if they exist
150 if (!string.IsNullOrEmpty(rulesText
))
151 streamWriter
.Write(rulesText
);
153 streamWriter
.Flush();
154 xomlBytesStream
.Position
= 0;
156 xomlHashCode
= MD5HashHelper
.ComputeHash(xomlBytesStream
.GetBuffer());
160 return LoadRootActivity(xomlText
, rulesText
, xomlHashCode
, false, initForRuntime
);
163 Activity root
= xomlFragments
.GetOrGenerateDefinition(null, xomlText
, rulesText
, xomlHashCode
, initForRuntime
, out exist
);
168 // Set the locking object used for cloning the definition
169 // for non-internal use (WorkflowInstance.GetWorkflowDefinition
170 // and WorkflowCompletedEventArgs.WorkflowDefinition)
171 WorkflowDefinitionLock
.SetWorkflowDefinitionLockObject(root
, new object());
173 EventHandler
<WorkflowDefinitionEventArgs
> localWorkflowDefinitionLoaded
= WorkflowDefinitionLoaded
;
174 if (localWorkflowDefinitionLoaded
!= null)
175 localWorkflowDefinitionLoaded(this.workflowRuntime
, new WorkflowDefinitionEventArgs(xomlHashCode
));
180 internal void ValidateDefinition(Activity root
, bool isNewType
, ITypeProvider typeProvider
)
182 if (!this.validateOnCreate
)
185 ValidationErrorCollection errors
= new ValidationErrorCollection();
187 // For validation purposes, create a type provider in the type case if the
188 // host did not push one.
189 if (typeProvider
== null)
190 typeProvider
= WorkflowRuntime
.CreateTypeProvider(root
);
192 // Validate that we are purely XAML.
195 if (!string.IsNullOrEmpty(root
.GetValue(WorkflowMarkupSerializer
.XClassProperty
) as string))
196 errors
.Add(new ValidationError(ExecutionStringManager
.XomlWorkflowHasClassName
, ErrorNumbers
.Error_XomlWorkflowHasClassName
));
198 Queue compositeActivities
= new Queue();
199 compositeActivities
.Enqueue(root
);
200 while (compositeActivities
.Count
> 0)
202 Activity activity
= compositeActivities
.Dequeue() as Activity
;
204 if (activity
.GetValue(WorkflowMarkupSerializer
.XCodeProperty
) != null)
205 errors
.Add(new ValidationError(ExecutionStringManager
.XomlWorkflowHasCode
, ErrorNumbers
.Error_XomlWorkflowHasCode
));
207 CompositeActivity compositeActivity
= activity
as CompositeActivity
;
208 if (compositeActivity
!= null)
210 foreach (Activity childActivity
in compositeActivity
.EnabledActivities
)
211 compositeActivities
.Enqueue(childActivity
);
216 ServiceContainer serviceContainer
= new ServiceContainer();
217 serviceContainer
.AddService(typeof(ITypeProvider
), typeProvider
);
219 ValidationManager validationManager
= new ValidationManager(serviceContainer
);
220 using (WorkflowCompilationContext
.CreateScope(validationManager
))
222 foreach (Validator validator
in validationManager
.GetValidators(root
.GetType()))
224 foreach (ValidationError error
in validator
.Validate(validationManager
, root
))
226 if (!error
.UserData
.Contains(typeof(Activity
)))
227 error
.UserData
[typeof(Activity
)] = root
;
233 if (errors
.HasErrors
)
234 throw new WorkflowValidationFailedException(ExecutionStringManager
.WorkflowValidationFailure
, errors
);
237 public void Dispose()
239 xomlFragments
.Dispose();
240 workflowTypes
.Dispose();
243 private Activity
LoadRootActivity(Type workflowType
, bool createDefinition
, bool initForRuntime
)
245 WorkflowLoaderService loader
= workflowRuntime
.GetService
<WorkflowLoaderService
>();
246 Activity root
= loader
.CreateInstance(workflowType
);
248 throw new InvalidOperationException(ExecutionStringManager
.CannotCreateRootActivity
);
249 if (root
.GetType() != workflowType
)
250 throw new InvalidOperationException(String
.Format(CultureInfo
.CurrentCulture
, ExecutionStringManager
.WorkflowTypeMismatch
, workflowType
.FullName
));
252 if (createDefinition
)
253 ValidateDefinition(root
, true, workflowRuntime
.GetService
<ITypeProvider
>());
256 ((IDependencyObjectAccessor
)root
).InitializeDefinitionForRuntime(null);
258 root
.SetValue(Activity
.WorkflowRuntimeProperty
, workflowRuntime
);
263 private Activity
LoadRootActivity(string xomlText
, string rulesText
, byte[] xomlHashCode
, bool createDefinition
, bool initForRuntime
)
265 Activity root
= null;
266 WorkflowLoaderService loader
= workflowRuntime
.GetService
<WorkflowLoaderService
>();
268 using (StringReader xomlTextReader
= new StringReader(xomlText
))
270 using (XmlReader xomlReader
= XmlReader
.Create(xomlTextReader
))
272 XmlReader rulesReader
= null;
273 StringReader rulesTextReader
= null;
276 if (!string.IsNullOrEmpty(rulesText
))
278 rulesTextReader
= new StringReader(rulesText
);
279 rulesReader
= XmlReader
.Create(rulesTextReader
);
281 root
= loader
.CreateInstance(xomlReader
, rulesReader
);
285 if (rulesReader
!= null)
287 if (rulesTextReader
!= null)
288 rulesTextReader
.Close();
294 throw new InvalidOperationException(ExecutionStringManager
.CannotCreateRootActivity
);
296 if (createDefinition
)
298 ITypeProvider typeProvider
= workflowRuntime
.GetService
<ITypeProvider
>();
299 ValidateDefinition(root
, false, typeProvider
);
303 ((IDependencyObjectAccessor
)root
).InitializeDefinitionForRuntime(null);
305 // Save the original markup.
306 root
.SetValue(Activity
.WorkflowXamlMarkupProperty
, xomlText
);
307 root
.SetValue(Activity
.WorkflowRulesMarkupProperty
, rulesText
);
308 root
.SetValue(WorkflowDefinitionHashCodeProperty
, xomlHashCode
);
309 root
.SetValue(Activity
.WorkflowRuntimeProperty
, workflowRuntime
);
314 private void CacheOutputParameters(Activity rootActivity
)
316 Type workflowType
= rootActivity
.GetType();
317 List
<PropertyInfo
> outputParameters
= null;
319 this.parametersLock
.AcquireWriterLock(-1);
323 if (this.workflowOutParameters
.ContainsKey(workflowType
))
326 // Cache negative and positive cases!
327 outputParameters
= new List
<PropertyInfo
>();
328 this.workflowOutParameters
.Add(workflowType
, outputParameters
);
330 PropertyInfo
[] properties
= workflowType
.GetProperties();
331 foreach (PropertyInfo property
in properties
)
333 if (!property
.CanRead
|| property
.DeclaringType
== typeof(DependencyObject
) || property
.DeclaringType
== typeof(Activity
) || property
.DeclaringType
== typeof(CompositeActivity
))
336 bool ignoreProperty
= false;
337 foreach (DependencyProperty dependencyProperty
in rootActivity
.MetaDependencyProperties
)
339 if (dependencyProperty
.Name
== property
.Name
&& dependencyProperty
.DefaultMetadata
.IsMetaProperty
)
341 ignoreProperty
= true;
347 outputParameters
.Add(property
);
352 Thread
.MemoryBarrier();
353 this.parametersLock
.ReleaseLock();
357 private enum CacheType
363 private class MruCache
: IDisposable
366 LinkedList
<Activity
> mruList
;
369 WorkflowDefinitionDispenser dispenser
;
372 internal MruCache(int capacity
, WorkflowDefinitionDispenser dispenser
, CacheType type
)
374 if (type
== CacheType
.Xoml
)
376 this.hashtable
= new Hashtable((IEqualityComparer
)new DigestComparerWrapper());
380 this.hashtable
= new Hashtable();
382 this.mruList
= new LinkedList
<Activity
>();
383 this.capacity
= capacity
;
384 this.dispenser
= dispenser
;
388 private void RemoveFromDictionary(Activity activity
)
390 byte[] key
= activity
.GetValue(WorkflowDefinitionHashCodeProperty
) as byte[];
393 this.hashtable
.Remove(key
);
397 Type type
= activity
.GetType();
398 this.hashtable
.Remove(type
);
402 private void AddToDictionary(LinkedListNode
<Activity
> node
)
404 byte[] key
= node
.Value
.GetValue(WorkflowDefinitionHashCodeProperty
) as byte[];
407 this.hashtable
.Add(key
, node
);
411 Type type
= node
.Value
.GetType();
412 this.hashtable
.Add(type
, node
);
416 internal Activity
GetDefinition(byte[] md5Codes
)
418 LinkedListNode
<Activity
> node
;
419 node
= this.hashtable
[md5Codes
] as LinkedListNode
<Activity
>;
430 internal Activity
GetOrGenerateDefinition(Type type
, string xomlText
, string rulesText
, byte[] md5Codes
, bool initForRuntime
, out bool exist
)
432 LinkedListNode
<Activity
> node
;
446 node
= this.hashtable
[key
] as LinkedListNode
<Activity
>;
452 node
= this.hashtable
[key
] as LinkedListNode
<Activity
>;
456 this.mruList
.Remove(node
);
457 this.mruList
.AddFirst(node
);
468 lock (this.hashtable
)
470 node
= this.hashtable
[key
] as LinkedListNode
<Activity
>;
476 this.mruList
.Remove(node
);
477 this.mruList
.AddFirst(node
);
486 activity
= this.dispenser
.LoadRootActivity(type
, true, initForRuntime
);
490 activity
= this.dispenser
.LoadRootActivity(xomlText
, rulesText
, key
as byte[], true, initForRuntime
);
494 if (this.size
< this.capacity
)
500 RemoveFromDictionary(this.mruList
.Last
.Value
);
501 this.mruList
.RemoveLast();
503 node
= new LinkedListNode
<Activity
>(activity
);
504 AddToDictionary(node
);
505 this.mruList
.AddFirst(node
);
513 Thread
.MemoryBarrier();
518 internal void GetWorkflowDefinitions
<K
>(out ReadOnlyCollection
<K
> keys
, out ReadOnlyCollection
<Activity
> values
)
520 lock (this.hashtable
)
522 if (((typeof(K
) == typeof(Type
)) && (this.type
== CacheType
.Type
)) || ((typeof(K
) == typeof(byte[])) && (this.type
== CacheType
.Xoml
)))
524 List
<K
> keyList
= new List
<K
>();
525 foreach (K key
in this.hashtable
.Keys
)
529 keys
= new ReadOnlyCollection
<K
>(keyList
);
530 List
<Activity
> list
= new List
<Activity
>();
531 foreach (LinkedListNode
<Activity
> node
in this.hashtable
.Values
)
533 list
.Add(node
.Value
);
535 values
= new ReadOnlyCollection
<Activity
>(list
);
545 public void Dispose()
547 foreach (LinkedListNode
<Activity
> node
in hashtable
.Values
)
551 node
.Value
.Dispose();
553 catch (Exception
)//ignore any dispose exception.
560 private class DigestComparerWrapper
: IEqualityComparer
562 IEqualityComparer
<byte[]> comparer
= (IEqualityComparer
<byte[]>)new DigestComparer();
563 bool IEqualityComparer
.Equals(object object1
, object object2
)
565 return comparer
.Equals((byte[])object1
, (byte[])object2
);
568 int IEqualityComparer
.GetHashCode(object obj
)
570 return comparer
.GetHashCode((byte[])obj
);
576 internal class WorkflowDefinitionLock
: IDisposable
578 internal static readonly DependencyProperty WorkflowDefinitionLockObjectProperty
= DependencyProperty
.RegisterAttached("WorkflowDefinitionLockObject", typeof(object), typeof(WorkflowDefinitionLock
), new PropertyMetadata(DependencyPropertyOptions
.NonSerialized
));
580 internal static object GetWorkflowDefinitionLockObject(DependencyObject dependencyObject
)
582 // The Dependency Properties are kept in a Dictionary<>, which is not thread safe between
583 // "getters" and "setters", so lock around the "getter", too.
584 lock (dependencyObject
)
586 return dependencyObject
.GetValue(WorkflowDefinitionLockObjectProperty
);
590 internal static void SetWorkflowDefinitionLockObject(DependencyObject dependencyObject
, object value)
592 lock (dependencyObject
)
594 if (dependencyObject
.GetValue(WorkflowDefinitionLockObjectProperty
) == null)
596 dependencyObject
.SetValue(WorkflowDefinitionLockObjectProperty
, value);
601 private object _syncObj
;
603 public WorkflowDefinitionLock(Activity definition
)
605 this._syncObj
= GetWorkflowDefinitionLockObject(definition
);
607 Debug
.Assert(this._syncObj
!= null, "Definition's synchronization object was null. This should always be set.");
609 #pragma warning disable 0618
611 Monitor
.Enter(this._syncObj
);
612 #pragma warning restore 0618
615 #region IDisposable Members
617 public void Dispose()
619 Monitor
.Exit(this._syncObj
);