Implement MachineKey.Protect and MachineKey.Unprotect
[mono.git] / mcs / class / System.ComponentModel.Composition / src / Composition.Initialization / System / ComponentModel / Composition / Hosting / DeploymentCatalog.cs
1 // -----------------------------------------------------------------------\r
2 // Copyright (c) Microsoft Corporation.  All rights reserved.\r
3 // -----------------------------------------------------------------------\r
4 using System;\r
5 using System.Collections.Generic;\r
6 using System.ComponentModel.Composition.Primitives;\r
7 using System.Linq;\r
8 using System.Net;\r
9 using System.Reflection;\r
10 using System.Threading;\r
11 using Microsoft.Internal;\r
12 \r
13 #if (SILVERLIGHT)\r
14 namespace System.ComponentModel.Composition.Hosting\r
15 {\r
16     /// <summary>\r
17     /// Implements a MEF catalog that supports Asynchronous download of Silverlast Xap files.\r
18     /// </summary>\r
19     public class DeploymentCatalog : ComposablePartCatalog, INotifyComposablePartCatalogChanged\r
20     {\r
21         static class State\r
22         {\r
23             public const int Created = 0;\r
24             public const int Initialized = 1000;\r
25             public const int DownloadStarted = 2000;\r
26             public const int DownloadCompleted = 3000;\r
27             public const int DownloadCancelled = 4000;\r
28         }\r
29 \r
30         private Lock _lock = new Lock();\r
31         private volatile bool _isDisposed = false;\r
32         private Uri _uri = null;\r
33         private int _state = State.Created;\r
34         private AggregateCatalog _catalogCollection = new AggregateCatalog();\r
35         private WebClient _webClient = null;\r
36 \r
37         /// <summary>\r
38         /// Construct a Deployment catalog with the parts from the main Xap.\r
39         /// </summary>\r
40         public DeploymentCatalog()\r
41         {\r
42             this.DiscoverParts(Package.CurrentAssemblies);\r
43             this._state = State.Initialized;\r
44         }\r
45 \r
46         /// <summary>\r
47         /// Construct a Deployment catalog with a string form relative uri.\r
48         /// </summary>\r
49         /// <value>\r
50         ///     A relative Uri to the Download Xap file\r
51         ///     <see cref="DeploymentCatalog"/>.\r
52         /// </value>\r
53         /// <exception cref="ArgumentException">\r
54         ///     The argument is null or an empty string.\r
55         /// </exception>\r
56         public DeploymentCatalog(string uriRelative)\r
57         {\r
58             Requires.NotNullOrEmpty(uriRelative, "uriRelative");\r
59             this._uri = new Uri(uriRelative, UriKind.Relative);\r
60         }\r
61 \r
62         /// <summary>\r
63         /// Construct a Deployment catalog with the parts from uri.\r
64         /// </summary>\r
65         /// <value>\r
66         ///     A Uri to the Download Xap file\r
67         ///     <see cref="System.Uri"/>.\r
68         /// </value>\r
69         /// <exception cref="ArgumentException">\r
70         ///     The argument is null.\r
71         /// </exception>\r
72         public DeploymentCatalog(Uri uri)\r
73         {\r
74             Requires.NotNull<Uri>(uri, "uri");\r
75             this._uri = uri;\r
76         }\r
77 \r
78         /// <summary>\r
79         /// Notify when the contents of the Catalog has changed.\r
80         /// </summary>\r
81         public event EventHandler<ComposablePartCatalogChangeEventArgs> Changed;\r
82 \r
83         /// <summary>\r
84         /// Notify when the contents of the Catalog is changing.\r
85         /// </summary>\r
86         public event EventHandler<ComposablePartCatalogChangeEventArgs> Changing;\r
87 \r
88         /// <summary>\r
89         /// Notify when the download has been completed.\r
90         /// </summary>\r
91         public event EventHandler<AsyncCompletedEventArgs> DownloadCompleted;\r
92 \r
93         /// <summary>\r
94         /// Notify when the contents of the Progress of the download has changed.\r
95         /// </summary>\r
96         public event EventHandler<DownloadProgressChangedEventArgs> DownloadProgressChanged;\r
97        \r
98         /// <summary>\r
99         /// Retrieve or create the WebClient.\r
100         /// </summary>\r
101         /// <exception cref="ObjectDisposedException">\r
102         ///     The <see cref="DeploymentCatalog"/> has been disposed of.\r
103         /// </exception>\r
104         private WebClient WebClient\r
105         {\r
106             get\r
107             {\r
108                 this.ThrowIfDisposed();\r
109                 if(this._webClient == null)\r
110                 {\r
111                     Interlocked.CompareExchange<WebClient>(ref this._webClient, new WebClient(), null);\r
112                 }\r
113                 return this._webClient;\r
114             }\r
115         }\r
116         \r
117         /// <summary>\r
118         ///     Gets the part definitions of the Deployment catalog.\r
119         /// </summary>\r
120         /// <value>\r
121         ///     A <see cref="IQueryable{T}"/> of <see cref="ComposablePartDefinition"/> objects of the \r
122         ///     <see cref="DeploymentCatalog"/>.\r
123         /// </value>\r
124         /// <exception cref="ObjectDisposedException">\r
125         ///     The <see cref="DeploymentCatalog"/> has been disposed of.\r
126         /// </exception>\r
127         public override IQueryable<ComposablePartDefinition> Parts\r
128         {\r
129             get\r
130             {\r
131                 this.ThrowIfDisposed();\r
132                 return this._catalogCollection.Parts;\r
133             }\r
134         }\r
135 \r
136         /// <summary>\r
137         ///     Gets the Uri of this catalog\r
138         /// </summary>\r
139         /// <exception cref="ObjectDisposedException">\r
140         ///     The <see cref="DeploymentCatalog"/> has been disposed of.\r
141         /// </exception>\r
142         public Uri Uri\r
143         {\r
144             get\r
145             {\r
146                 this.ThrowIfDisposed();\r
147                 return this._uri;\r
148             }\r
149         }\r
150 \r
151         /// <summary>\r
152         /// </summary>\r
153         /// <param name="assemblies">\r
154         /// </param>\r
155         /// <exception cref="ObjectDisposedException">\r
156         ///     The <see cref="DeploymentCatalog"/> has been disposed of.\r
157         /// </exception>\r
158         private void DiscoverParts(IEnumerable<Assembly> assemblies)\r
159         {\r
160             this.ThrowIfDisposed();\r
161 \r
162             var addedDefinitions = new List<ComposablePartDefinition>();\r
163             var addedCatalogs = new Dictionary<string, ComposablePartCatalog>();\r
164             using(new ReadLock(this._lock))\r
165             {\r
166                 foreach (var assembly in assemblies)\r
167                 {\r
168                     if (addedCatalogs.ContainsKey(assembly.FullName)) \r
169                     {\r
170                         // Nothing to do because the assembly has already been added.\r
171                         continue;\r
172                     }\r
173 \r
174                     var catalog = new AssemblyCatalog(assembly);\r
175                     addedDefinitions.AddRange(catalog.Parts);\r
176                     addedCatalogs.Add(assembly.FullName, catalog);\r
177                 }\r
178             }\r
179 \r
180             // Generate notifications\r
181             using (var atomicComposition = new AtomicComposition())\r
182             {\r
183                 var changingArgs = new ComposablePartCatalogChangeEventArgs(addedDefinitions, Enumerable.Empty<ComposablePartDefinition>(), atomicComposition);\r
184                 this.OnChanging(changingArgs);\r
185 \r
186                 using (new WriteLock(this._lock))\r
187                 {\r
188                     foreach (var item in addedCatalogs)\r
189                     {\r
190                         this._catalogCollection.Catalogs.Add(item.Value);\r
191                     }\r
192                 }\r
193                 atomicComposition.Complete();\r
194             }\r
195 \r
196             var changedArgs = new ComposablePartCatalogChangeEventArgs(addedDefinitions, Enumerable.Empty<ComposablePartDefinition>(), null);\r
197             this.OnChanged(changedArgs);\r
198         }\r
199 \r
200         /// <summary>\r
201         ///     Returns the export definitions that match the constraint defined by the specified definition.\r
202         /// </summary>\r
203         /// <param name="definition">\r
204         ///     The <see cref="ImportDefinition"/> that defines the conditions of the \r
205         ///     <see cref="ExportDefinition"/> objects to return.\r
206         /// </param>\r
207         /// <returns>\r
208         ///     An <see cref="IEnumerable{T}"/> of <see cref="Tuple{T1, T2}"/> containing the \r
209         ///     <see cref="ExportDefinition"/> objects and their associated \r
210         ///     <see cref="ComposablePartDefinition"/> for objects that match the constraint defined \r
211         ///     by <paramref name="definition"/>.\r
212         /// </returns>\r
213         /// <exception cref="ArgumentNullException">\r
214         ///     <paramref name="definition"/> is <see langword="null"/>.\r
215         /// </exception>\r
216         /// <exception cref="ObjectDisposedException">\r
217         ///     The <see cref="DeploymentCatalog"/> has been disposed of.\r
218         /// </exception>\r
219         public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition)\r
220         {\r
221             this.ThrowIfDisposed();\r
222             Requires.NotNull(definition, "definition");\r
223 \r
224             return this._catalogCollection.GetExports(definition);\r
225         }\r
226 \r
227         /// <summary>\r
228         /// Cancel the async operation.\r
229         /// </summary>\r
230         public void CancelAsync()\r
231         {\r
232             ThrowIfDisposed();\r
233             MutateStateOrThrow(State.DownloadCancelled, State.DownloadStarted);\r
234             this.WebClient.CancelAsync();\r
235         }\r
236 \r
237         /// <summary>\r
238         /// Begin the asynchronous download.\r
239         /// </summary>\r
240         public void DownloadAsync()\r
241         {\r
242             ThrowIfDisposed();\r
243 \r
244             if (Interlocked.CompareExchange(ref this._state, State.DownloadStarted, State.Created) == State.Created)\r
245             {\r
246                 // Created with Downloadable content do download\r
247                 this.WebClient.OpenReadCompleted += new OpenReadCompletedEventHandler(HandleOpenReadCompleted);\r
248                 this.WebClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(HandleDownloadProgressChanged);\r
249                 this.WebClient.OpenReadAsync(Uri, this);\r
250             }\r
251             else\r
252             {\r
253                 // Created with LocalAssemblies \r
254                 MutateStateOrThrow(State.DownloadCompleted, State.Initialized);\r
255 \r
256                 this.OnDownloadCompleted(new AsyncCompletedEventArgs(null, false, this));\r
257             }\r
258         }\r
259 \r
260         void HandleDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)\r
261         {\r
262             EventHandler<DownloadProgressChangedEventArgs> downloadProgressChangedEvent = this.DownloadProgressChanged;\r
263             if (downloadProgressChangedEvent != null)\r
264             {\r
265                 downloadProgressChangedEvent(this, e);\r
266             }\r
267         }\r
268 \r
269         private void HandleOpenReadCompleted(object sender, OpenReadCompletedEventArgs e)\r
270         {\r
271             Exception error = e.Error;\r
272             bool cancelled = e.Cancelled;\r
273 \r
274             // Possible valid current states are DownloadStarted and DownloadCancelled.\r
275             int currentState = Interlocked.CompareExchange(ref this._state, State.DownloadCompleted, State.DownloadStarted);\r
276 \r
277             if (currentState != State.DownloadStarted)\r
278             {\r
279                 cancelled = true;\r
280             }\r
281 \r
282             if (error == null && !cancelled)\r
283             {\r
284                 try\r
285                 {\r
286                     var assemblies = Package.LoadPackagedAssemblies(e.Result);\r
287                     this.DiscoverParts(assemblies);\r
288                 }\r
289                 catch (Exception ex)\r
290                 {\r
291                     error = new InvalidOperationException(Strings.InvalidOperationException_ErrorReadingXap, ex);\r
292                 }\r
293             }\r
294 \r
295             this.OnDownloadCompleted(new AsyncCompletedEventArgs(error, cancelled, this));\r
296         }\r
297 \r
298         /// <summary>\r
299         ///     Raises the <see cref="INotifyComposablePartCatalogChanged.Changed"/> event.\r
300         /// </summary>\r
301         /// <param name="e">\r
302         ///     An <see cref="ComposablePartCatalogChangeEventArgs"/> containing the data for the event.\r
303         /// </param>\r
304         protected virtual void OnChanged(ComposablePartCatalogChangeEventArgs e)\r
305         {\r
306             EventHandler<ComposablePartCatalogChangeEventArgs> changedEvent = this.Changed;\r
307             if (changedEvent != null)\r
308             {\r
309                 changedEvent(this, e);\r
310             }\r
311         }\r
312 \r
313         /// <summary>\r
314         ///     Raises the <see cref="INotifyComposablePartCatalogChanged.Changing"/> event.\r
315         /// </summary>\r
316         /// <param name="e">\r
317         ///     An <see cref="ComposablePartCatalogChangeEventArgs"/> containing the data for the event.\r
318         /// </param>\r
319         protected virtual void OnChanging(ComposablePartCatalogChangeEventArgs e)\r
320         {\r
321             EventHandler<ComposablePartCatalogChangeEventArgs> changingEvent = this.Changing;\r
322             if (changingEvent != null)\r
323             {\r
324                 changingEvent(this, e);\r
325             }\r
326         }\r
327 \r
328         /// <summary>\r
329         ///     Raises the <see cref="DownloadCompleted"/> event.\r
330         /// </summary>\r
331         /// <param name="e">\r
332         ///     An <see cref="AsyncCompletedEventArgs"/> containing the data for the event.\r
333         /// </param>\r
334         protected virtual void OnDownloadCompleted(AsyncCompletedEventArgs e)\r
335         {\r
336             EventHandler<AsyncCompletedEventArgs> downloadCompletedEvent = this.DownloadCompleted;\r
337             if (downloadCompletedEvent != null)\r
338             {\r
339                 downloadCompletedEvent(this, e);\r
340             }\r
341         }\r
342 \r
343         /// <summary>\r
344         ///     Raises the <see cref="DownloadProgressChanged"/> event.\r
345         /// </summary>\r
346         /// <param name="e">\r
347         ///     An <see cref="ProgressChangedEventArgs"/> containing the data for the event.\r
348         /// </param>\r
349         protected virtual void OnDownloadProgressChanged(DownloadProgressChangedEventArgs e)\r
350         {\r
351             EventHandler<DownloadProgressChangedEventArgs> downloadProgressChangedEvent = this.DownloadProgressChanged;\r
352             if (downloadProgressChangedEvent != null)\r
353             {\r
354                 downloadProgressChangedEvent(this, e);\r
355             }\r
356         }\r
357 \r
358         protected override void Dispose(bool disposing)\r
359         {\r
360             try\r
361             {\r
362                 if (disposing)\r
363                 {\r
364                     if (!this._isDisposed)\r
365                     {\r
366                         AggregateCatalog catalogs = null;\r
367                         bool disposeLock = false;\r
368                         try\r
369                         {\r
370                             using (new WriteLock(this._lock))\r
371                             {\r
372                                 if (!this._isDisposed)\r
373                                 {\r
374                                     disposeLock = true;\r
375                                     catalogs = this._catalogCollection;\r
376                                     this._catalogCollection = null;\r
377                                     this._isDisposed = true;\r
378                                 }\r
379                             }\r
380                         }\r
381                         finally\r
382                         {\r
383                             if (catalogs != null)\r
384                             {\r
385                                 catalogs.Dispose();\r
386                             }\r
387 \r
388                             if (disposeLock)\r
389                             {\r
390                                 this._lock.Dispose();\r
391                             }\r
392                         }\r
393                     }\r
394                 }\r
395             }\r
396             finally\r
397             {\r
398                 base.Dispose(disposing);\r
399             }\r
400         }\r
401 \r
402         private void ThrowIfDisposed()\r
403         {\r
404             if (this._isDisposed)\r
405             {\r
406                 throw new ObjectDisposedException(this.GetType().ToString()); \r
407             }\r
408         }\r
409 \r
410         private void MutateStateOrThrow(int toState, int fromState)\r
411         {\r
412             int currentState = Interlocked.CompareExchange(ref this._state, toState, fromState);\r
413             if(currentState != fromState)\r
414             {\r
415                 throw new InvalidOperationException(Strings.InvalidOperationException_DeploymentCatalogInvalidStateChange);\r
416             }\r
417         }\r
418     }\r
419 }\r
420 #endif\r