update MEF to preview 9
[mcs.git] / class / System.ComponentModel.Composition / src / Composition.Initialization / System / ComponentModel / Composition / Hosting / DeploymentCatalog.cs
blobe6042b1caa1980d4199440bf83cfdbf65fcb8a37
1 // -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
4 using System;
5 using System.Collections.Generic;
6 using System.ComponentModel.Composition.Primitives;
7 using System.Linq;
8 using System.Net;
9 using System.Reflection;
10 using System.Threading;
11 using Microsoft.Internal;
13 #if (SILVERLIGHT)
14 namespace System.ComponentModel.Composition.Hosting
16 /// <summary>
17 /// Implements a MEF catalog that supports Asynchronous download of Silverlast Xap files.
18 /// </summary>
19 public class DeploymentCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged
21 static class State
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;
37 /// <summary>
38 /// Construct a Deployment catalog with the parts from the main Xap.
39 /// </summary>
40 public DeploymentCatalog()
42 this.DiscoverParts(Package.CurrentAssemblies);
43 this._state = State.Initialized;
46 /// <summary>
47 /// Construct a Deployment catalog with a string form relative uri.
48 /// </summary>
49 /// <value>
50 /// A relative Uri to the Download Xap file
51 /// <see cref="DeploymentCatalog"/>.
52 /// </value>
53 /// <exception cref="ArgumentException">
54 /// The argument is null or an empty string.
55 /// </exception>
56 public DeploymentCatalog(string uriRelative)
58 Requires.NotNullOrEmpty(uriRelative, "uriRelative");
59 this._uri = new Uri(uriRelative, UriKind.Relative);
62 /// <summary>
63 /// Construct a Deployment catalog with the parts from uri.
64 /// </summary>
65 /// <value>
66 /// A Uri to the Download Xap file
67 /// <see cref="System.Uri"/>.
68 /// </value>
69 /// <exception cref="ArgumentException">
70 /// The argument is null.
71 /// </exception>
72 public DeploymentCatalog(Uri uri)
74 Requires.NotNull<Uri>(uri, "uri");
75 this._uri = uri;
78 /// <summary>
79 /// Notify when the contents of the Catalog has changed.
80 /// </summary>
81 public event EventHandler<ComposablePartCatalogChangeEventArgs> Changed;
83 /// <summary>
84 /// Notify when the contents of the Catalog is changing.
85 /// </summary>
86 public event EventHandler<ComposablePartCatalogChangeEventArgs> Changing;
88 /// <summary>
89 /// Notify when the download has been completed.
90 /// </summary>
91 public event EventHandler<AsyncCompletedEventArgs> DownloadCompleted;
93 /// <summary>
94 /// Notify when the contents of the Progress of the download has changed.
95 /// </summary>
96 public event EventHandler<DownloadProgressChangedEventArgs> DownloadProgressChanged;
98 /// <summary>
99 /// Retrieve or create the WebClient.
100 /// </summary>
101 /// <exception cref="ObjectDisposedException">
102 /// The <see cref="DeploymentCatalog"/> has been disposed of.
103 /// </exception>
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;
117 /// <summary>
118 /// Gets the part definitions of the Deployment catalog.
119 /// </summary>
120 /// <value>
121 /// A <see cref="IQueryable{T}"/> of <see cref="ComposablePartDefinition"/> objects of the
122 /// <see cref="DeploymentCatalog"/>.
123 /// </value>
124 /// <exception cref="ObjectDisposedException">
125 /// The <see cref="DeploymentCatalog"/> has been disposed of.
126 /// </exception>
127 public override IQueryable<ComposablePartDefinition> Parts
131 this.ThrowIfDisposed();
132 return this._catalogCollection.Parts;
136 /// <summary>
137 /// Gets the Uri of this catalog
138 /// </summary>
139 /// <exception cref="ObjectDisposedException">
140 /// The <see cref="DeploymentCatalog"/> has been disposed of.
141 /// </exception>
142 public Uri Uri
146 this.ThrowIfDisposed();
147 return this._uri;
151 /// <summary>
152 /// </summary>
153 /// <param name="assemblies">
154 /// </param>
155 /// <exception cref="ObjectDisposedException">
156 /// The <see cref="DeploymentCatalog"/> has been disposed of.
157 /// </exception>
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.
171 continue;
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);
200 /// <summary>
201 /// Returns the export definitions that match the constraint defined by the specified definition.
202 /// </summary>
203 /// <param name="definition">
204 /// The <see cref="ImportDefinition"/> that defines the conditions of the
205 /// <see cref="ExportDefinition"/> objects to return.
206 /// </param>
207 /// <returns>
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"/>.
212 /// </returns>
213 /// <exception cref="ArgumentNullException">
214 /// <paramref name="definition"/> is <see langword="null"/>.
215 /// </exception>
216 /// <exception cref="ObjectDisposedException">
217 /// The <see cref="DeploymentCatalog"/> has been disposed of.
218 /// </exception>
219 public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition)
221 this.ThrowIfDisposed();
222 Requires.NotNull(definition, "definition");
224 return this._catalogCollection.GetExports(definition);
227 /// <summary>
228 /// Cancel the async operation.
229 /// </summary>
230 public void CancelAsync()
232 ThrowIfDisposed();
233 MutateStateOrThrow(State.DownloadCancelled, State.DownloadStarted);
234 this.WebClient.CancelAsync();
237 /// <summary>
238 /// Begin the asynchronous download.
239 /// </summary>
240 public void DownloadAsync()
242 ThrowIfDisposed();
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);
251 else
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)
279 cancelled = true;
282 if (error == null && !cancelled)
286 var assemblies = Package.LoadPackagedAssemblies(e.Result);
287 this.DiscoverParts(assemblies);
289 catch (Exception ex)
291 error = new InvalidOperationException(Strings.InvalidOperationException_ErrorReadingXap, ex);
295 this.OnDownloadCompleted(new AsyncCompletedEventArgs(error, cancelled, this));
298 /// <summary>
299 /// Raises the <see cref="INotifyComposablePartCatalogChanged.Changed"/> event.
300 /// </summary>
301 /// <param name="e">
302 /// An <see cref="ComposablePartCatalogChangeEventArgs"/> containing the data for the event.
303 /// </param>
304 protected virtual void OnChanged(ComposablePartCatalogChangeEventArgs e)
306 EventHandler<ComposablePartCatalogChangeEventArgs> changedEvent = this.Changed;
307 if (changedEvent != null)
309 changedEvent(this, e);
313 /// <summary>
314 /// Raises the <see cref="INotifyComposablePartCatalogChanged.Changing"/> event.
315 /// </summary>
316 /// <param name="e">
317 /// An <see cref="ComposablePartCatalogChangeEventArgs"/> containing the data for the event.
318 /// </param>
319 protected virtual void OnChanging(ComposablePartCatalogChangeEventArgs e)
321 EventHandler<ComposablePartCatalogChangeEventArgs> changingEvent = this.Changing;
322 if (changingEvent != null)
324 changingEvent(this, e);
328 /// <summary>
329 /// Raises the <see cref="DownloadCompleted"/> event.
330 /// </summary>
331 /// <param name="e">
332 /// An <see cref="AsyncCompletedEventArgs"/> containing the data for the event.
333 /// </param>
334 protected virtual void OnDownloadCompleted(AsyncCompletedEventArgs e)
336 EventHandler<AsyncCompletedEventArgs> downloadCompletedEvent = this.DownloadCompleted;
337 if (downloadCompletedEvent != null)
339 downloadCompletedEvent(this, e);
343 /// <summary>
344 /// Raises the <see cref="DownloadProgressChanged"/> event.
345 /// </summary>
346 /// <param name="e">
347 /// An <see cref="ProgressChangedEventArgs"/> containing the data for the event.
348 /// </param>
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)
362 if (disposing)
364 if (!this._isDisposed)
366 AggregateCatalog catalogs = null;
367 bool disposeLock = false;
370 using (new WriteLock(this._lock))
372 if (!this._isDisposed)
374 disposeLock = true;
375 catalogs = this._catalogCollection;
376 this._catalogCollection = null;
377 this._isDisposed = true;
381 finally
383 if (catalogs != null)
385 catalogs.Dispose();
388 if (disposeLock)
390 this._lock.Dispose();
396 finally
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);
420 #endif