1
// -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
5 using System
.Collections
.Generic
;
6 using System
.ComponentModel
.Composition
.Primitives
;
9 using System
.Reflection
;
10 using System
.Threading
;
11 using Microsoft
.Internal
;
14 namespace System
.ComponentModel
.Composition
.Hosting
17 /// Implements a MEF catalog that supports Asynchronous download of Silverlast Xap files.
19 public class DeploymentCatalog
: ComposablePartCatalog
, INotifyComposablePartCatalogChanged
23 public const int Created
= 0;
24 public const int Initialized
= 1000;
25 public const int DownloadStarted
= 2000;
26 public const int DownloadCompleted
= 3000;
27 public const int DownloadCancelled
= 4000;
30 private Lock _lock
= new Lock();
31 private volatile bool _isDisposed
= false;
32 private Uri _uri
= null;
33 private int _state
= State
.Created
;
34 private AggregateCatalog _catalogCollection
= new AggregateCatalog();
35 private WebClient _webClient
= null;
38 /// Construct a Deployment catalog with the parts from the main Xap.
40 public DeploymentCatalog()
42 this.DiscoverParts(Package
.CurrentAssemblies
);
43 this._state
= State
.Initialized
;
47 /// Construct a Deployment catalog with a string form relative uri.
50 /// A relative Uri to the Download Xap file
51 /// <see cref="DeploymentCatalog"/>.
53 /// <exception cref="ArgumentException">
54 /// The argument is null or an empty string.
56 public DeploymentCatalog(string uriRelative
)
58 Requires
.NotNullOrEmpty(uriRelative
, "uriRelative");
59 this._uri
= new Uri(uriRelative
, UriKind
.Relative
);
63 /// Construct a Deployment catalog with the parts from uri.
66 /// A Uri to the Download Xap file
67 /// <see cref="System.Uri"/>.
69 /// <exception cref="ArgumentException">
70 /// The argument is null.
72 public DeploymentCatalog(Uri uri
)
74 Requires
.NotNull
<Uri
>(uri
, "uri");
79 /// Notify when the contents of the Catalog has changed.
81 public event EventHandler
<ComposablePartCatalogChangeEventArgs
> Changed
;
84 /// Notify when the contents of the Catalog is changing.
86 public event EventHandler
<ComposablePartCatalogChangeEventArgs
> Changing
;
89 /// Notify when the download has been completed.
91 public event EventHandler
<AsyncCompletedEventArgs
> DownloadCompleted
;
94 /// Notify when the contents of the Progress of the download has changed.
96 public event EventHandler
<DownloadProgressChangedEventArgs
> DownloadProgressChanged
;
99 /// Retrieve or create the WebClient.
101 /// <exception cref="ObjectDisposedException">
102 /// The <see cref="DeploymentCatalog"/> has been disposed of.
104 private WebClient WebClient
108 this.ThrowIfDisposed();
109 if(this._webClient
== null)
111 Interlocked
.CompareExchange
<WebClient
>(ref this._webClient
, new WebClient(), null);
113 return this._webClient
;
118 /// Gets the part definitions of the Deployment catalog.
121 /// A <see cref="IQueryable{T}"/> of <see cref="ComposablePartDefinition"/> objects of the
122 /// <see cref="DeploymentCatalog"/>.
124 /// <exception cref="ObjectDisposedException">
125 /// The <see cref="DeploymentCatalog"/> has been disposed of.
127 public override IQueryable
<ComposablePartDefinition
> Parts
131 this.ThrowIfDisposed();
132 return this._catalogCollection
.Parts
;
137 /// Gets the Uri of this catalog
139 /// <exception cref="ObjectDisposedException">
140 /// The <see cref="DeploymentCatalog"/> has been disposed of.
146 this.ThrowIfDisposed();
153 /// <param name="assemblies">
155 /// <exception cref="ObjectDisposedException">
156 /// The <see cref="DeploymentCatalog"/> has been disposed of.
158 private void DiscoverParts(IEnumerable
<Assembly
> assemblies
)
160 this.ThrowIfDisposed();
162 var addedDefinitions
= new List
<ComposablePartDefinition
>();
163 var addedCatalogs
= new Dictionary
<string, ComposablePartCatalog
>();
164 using(new ReadLock(this._lock
))
166 foreach (var assembly
in assemblies
)
168 if (addedCatalogs
.ContainsKey(assembly
.FullName
))
170 // Nothing to do because the assembly has already been added.
174 var catalog
= new AssemblyCatalog(assembly
);
175 addedDefinitions
.AddRange(catalog
.Parts
);
176 addedCatalogs
.Add(assembly
.FullName
, catalog
);
180 // Generate notifications
181 using (var atomicComposition
= new AtomicComposition())
183 var changingArgs
= new ComposablePartCatalogChangeEventArgs(addedDefinitions
, Enumerable
.Empty
<ComposablePartDefinition
>(), atomicComposition
);
184 this.OnChanging(changingArgs
);
186 using (new WriteLock(this._lock
))
188 foreach (var item
in addedCatalogs
)
190 this._catalogCollection
.Catalogs
.Add(item
.Value
);
193 atomicComposition
.Complete();
196 var changedArgs
= new ComposablePartCatalogChangeEventArgs(addedDefinitions
, Enumerable
.Empty
<ComposablePartDefinition
>(), null);
197 this.OnChanged(changedArgs
);
201 /// Returns the export definitions that match the constraint defined by the specified definition.
203 /// <param name="definition">
204 /// The <see cref="ImportDefinition"/> that defines the conditions of the
205 /// <see cref="ExportDefinition"/> objects to return.
208 /// An <see cref="IEnumerable{T}"/> of <see cref="Tuple{T1, T2}"/> containing the
209 /// <see cref="ExportDefinition"/> objects and their associated
210 /// <see cref="ComposablePartDefinition"/> for objects that match the constraint defined
211 /// by <paramref name="definition"/>.
213 /// <exception cref="ArgumentNullException">
214 /// <paramref name="definition"/> is <see langword="null"/>.
216 /// <exception cref="ObjectDisposedException">
217 /// The <see cref="DeploymentCatalog"/> has been disposed of.
219 public override IEnumerable
<Tuple
<ComposablePartDefinition
, ExportDefinition
>> GetExports(ImportDefinition definition
)
221 this.ThrowIfDisposed();
222 Requires
.NotNull(definition
, "definition");
224 return this._catalogCollection
.GetExports(definition
);
228 /// Cancel the async operation.
230 public void CancelAsync()
233 MutateStateOrThrow(State
.DownloadCancelled
, State
.DownloadStarted
);
234 this.WebClient
.CancelAsync();
238 /// Begin the asynchronous download.
240 public void DownloadAsync()
244 if (Interlocked
.CompareExchange(ref this._state
, State
.DownloadStarted
, State
.Created
) == State
.Created
)
246 // Created with Downloadable content do download
247 this.WebClient
.OpenReadCompleted
+= new OpenReadCompletedEventHandler(HandleOpenReadCompleted
);
248 this.WebClient
.DownloadProgressChanged
+= new DownloadProgressChangedEventHandler(HandleDownloadProgressChanged
);
249 this.WebClient
.OpenReadAsync(Uri
, this);
253 // Created with LocalAssemblies
254 MutateStateOrThrow(State
.DownloadCompleted
, State
.Initialized
);
256 this.OnDownloadCompleted(new AsyncCompletedEventArgs(null, false, this));
260 void HandleDownloadProgressChanged(object sender
, DownloadProgressChangedEventArgs e
)
262 EventHandler
<DownloadProgressChangedEventArgs
> downloadProgressChangedEvent
= this.DownloadProgressChanged
;
263 if (downloadProgressChangedEvent
!= null)
265 downloadProgressChangedEvent(this, e
);
269 private void HandleOpenReadCompleted(object sender
, OpenReadCompletedEventArgs e
)
271 Exception error
= e
.Error
;
272 bool cancelled
= e
.Cancelled
;
274 // Possible valid current states are DownloadStarted and DownloadCancelled.
275 int currentState
= Interlocked
.CompareExchange(ref this._state
, State
.DownloadCompleted
, State
.DownloadStarted
);
277 if (currentState
!= State
.DownloadStarted
)
282 if (error
== null && !cancelled
)
286 var assemblies
= Package
.LoadPackagedAssemblies(e
.Result
);
287 this.DiscoverParts(assemblies
);
291 error
= new InvalidOperationException(Strings
.InvalidOperationException_ErrorReadingXap
, ex
);
295 this.OnDownloadCompleted(new AsyncCompletedEventArgs(error
, cancelled
, this));
299 /// Raises the <see cref="INotifyComposablePartCatalogChanged.Changed"/> event.
302 /// An <see cref="ComposablePartCatalogChangeEventArgs"/> containing the data for the event.
304 protected virtual void OnChanged(ComposablePartCatalogChangeEventArgs e
)
306 EventHandler
<ComposablePartCatalogChangeEventArgs
> changedEvent
= this.Changed
;
307 if (changedEvent
!= null)
309 changedEvent(this, e
);
314 /// Raises the <see cref="INotifyComposablePartCatalogChanged.Changing"/> event.
317 /// An <see cref="ComposablePartCatalogChangeEventArgs"/> containing the data for the event.
319 protected virtual void OnChanging(ComposablePartCatalogChangeEventArgs e
)
321 EventHandler
<ComposablePartCatalogChangeEventArgs
> changingEvent
= this.Changing
;
322 if (changingEvent
!= null)
324 changingEvent(this, e
);
329 /// Raises the <see cref="DownloadCompleted"/> event.
332 /// An <see cref="AsyncCompletedEventArgs"/> containing the data for the event.
334 protected virtual void OnDownloadCompleted(AsyncCompletedEventArgs e
)
336 EventHandler
<AsyncCompletedEventArgs
> downloadCompletedEvent
= this.DownloadCompleted
;
337 if (downloadCompletedEvent
!= null)
339 downloadCompletedEvent(this, e
);
344 /// Raises the <see cref="DownloadProgressChanged"/> event.
347 /// An <see cref="ProgressChangedEventArgs"/> containing the data for the event.
349 protected virtual void OnDownloadProgressChanged(DownloadProgressChangedEventArgs e
)
351 EventHandler
<DownloadProgressChangedEventArgs
> downloadProgressChangedEvent
= this.DownloadProgressChanged
;
352 if (downloadProgressChangedEvent
!= null)
354 downloadProgressChangedEvent(this, e
);
358 protected override void Dispose(bool disposing
)
364 if (!this._isDisposed
)
366 AggregateCatalog catalogs
= null;
367 bool disposeLock
= false;
370 using (new WriteLock(this._lock
))
372 if (!this._isDisposed
)
375 catalogs
= this._catalogCollection
;
376 this._catalogCollection
= null;
377 this._isDisposed
= true;
383 if (catalogs
!= null)
390 this._lock
.Dispose();
398 base.Dispose(disposing
);
402 private void ThrowIfDisposed()
404 if (this._isDisposed
)
406 throw new ObjectDisposedException(this.GetType().ToString());
410 private void MutateStateOrThrow(int toState
, int fromState
)
412 int currentState
= Interlocked
.CompareExchange(ref this._state
, toState
, fromState
);
413 if(currentState
!= fromState
)
415 throw new InvalidOperationException(Strings
.InvalidOperationException_DeploymentCatalogInvalidStateChange
);