2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.PeerResolvers / CustomPeerResolverService.cs
1 // 
2 // CustomPeerResolverService.cs
3 // 
4 // Author: 
5 //     Marcos Cobena (marcoscobena@gmail.com)
6 //      Atsushi Enomoto  <atsushi@ximian.com>
7 // 
8 // Copyright 2007 Marcos Cobena (http://www.youcannoteatbits.org/)
9 //
10 // Copyright (C) 2009 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Collections.Generic;
33 using System.IO;
34 using System.Linq;
35 using System.Net.Sockets;
36 using System.Transactions;
37 using System.Timers;
38
39 namespace System.ServiceModel.PeerResolvers
40 {
41 #if false
42         [MonoTODO ("Implement cleanup and refresh")]
43         // FIXME: TransactionTimeout must be null by-default.
44         [ServiceBehavior (AutomaticSessionShutdown = true, ConcurrencyMode = ConcurrencyMode.Multiple, 
45                           InstanceContextMode = InstanceContextMode.Single, ReleaseServiceInstanceOnTransactionComplete = true, 
46                           TransactionIsolationLevel = IsolationLevel.Unspecified, /*TransactionTimeout = null, */
47                           UseSynchronizationContext = false, ValidateMustUnderstand = true)]
48         public class CustomPeerResolverService : IPeerResolverContract
49         {
50                 bool control_shape;
51                 bool opened;
52                 // Maybe it's worth to change List<T> for a better distributed and faster collection.
53                 List<Node> mesh = new List<Node> ();
54                 object mesh_lock = new object ();
55                 Timer refresh_timer, cleanup_timer;
56                 DateTime last_refresh_time = DateTime.Now;
57
58                 public CustomPeerResolverService ()
59                 {
60                         refresh_timer = new Timer () { AutoReset = true };
61                         RefreshInterval = new TimeSpan (0, 10, 0);
62                         refresh_timer.Elapsed += delegate {
63                                         // FIXME: implement
64                                 };
65                         cleanup_timer = new Timer () { AutoReset = true };
66                         CleanupInterval = new TimeSpan (0, 1, 0);
67                         cleanup_timer.Elapsed += delegate {
68                                         // FIXME: implement
69                                 };
70                         control_shape = false;
71                         opened = false;
72                 }
73
74                 public TimeSpan CleanupInterval {
75                         get { return TimeSpan.FromMilliseconds ((int) cleanup_timer.Interval); }
76                         set {
77                                 if ((value < TimeSpan.Zero) || (value > TimeSpan.MaxValue))
78                                         throw new ArgumentOutOfRangeException (
79                                         "The interval is either zero or greater than max value.");
80                                 if (opened)
81                                         throw new InvalidOperationException ("The interval must be set before it is opened");
82
83                                 cleanup_timer.Interval = value.TotalMilliseconds;
84                         }
85                 }
86
87                 public bool ControlShape {
88                         get { return control_shape; }
89                         set { control_shape = value; }
90                 }
91
92                 public TimeSpan RefreshInterval {
93                         get { return TimeSpan.FromMilliseconds ((int) refresh_timer.Interval); }
94                         set {
95                                 if ((value < TimeSpan.Zero) || (value > TimeSpan.MaxValue))
96                                         throw new ArgumentOutOfRangeException (
97                                         "The interval is either zero or greater than max value.");
98                                 if (opened)
99                                         throw new InvalidOperationException ("The interval must be set before it is opened");
100
101                                 refresh_timer.Interval = value.TotalMilliseconds;
102                         }
103                 }
104
105                 [MonoTODO ("Do we have to unregister nodes here?")]
106                 public virtual void Close ()
107                 {
108                         if (! opened)
109                                 throw new InvalidOperationException ("The service has never been opened or it was closed by a previous call to this method.");
110                         refresh_timer.Stop ();
111                         cleanup_timer.Stop ();
112                 }
113
114                 [MonoTODO]
115                 public virtual ServiceSettingsResponseInfo GetServiceSettings ()
116                 {
117                         if (! opened)
118                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
119                 
120 //                      return new ServiceSettingsResponseInfo ();
121                         throw new NotImplementedException ();
122                 }
123
124                 public virtual void Open ()
125                 {
126                         if ((CleanupInterval == TimeSpan.Zero) || (RefreshInterval == TimeSpan.Zero))
127                                 throw new ArgumentException ("Cleanup interval or refresh interval are set to a time span interval of zero.");
128
129                         if (opened)
130                                 throw new InvalidOperationException ("The service has been started by a previous call to this method.");
131                         
132                         opened = true;
133
134                         refresh_timer.Start ();
135                         cleanup_timer.Start ();
136                 }
137
138                 [MonoTODO]
139                 public virtual RefreshResponseInfo Refresh (RefreshInfo refreshInfo)
140                 {
141                         if (refreshInfo == null)
142                                 throw new ArgumentException ("Refresh info cannot be null.");
143                         
144                         if (! opened)
145                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
146
147                         var node = mesh.FirstOrDefault (n => n.MeshId == refreshInfo.MeshId && n.RegistrationId.Equals (refreshInfo.RegistrationId));
148                         if (node == null)
149                                 return new RefreshResponseInfo (TimeSpan.Zero, RefreshResult.RegistrationNotFound);
150
151                         // FIXME: implement actual refresh.
152                         last_refresh_time = DateTime.Now;
153
154                         return new RefreshResponseInfo (RefreshInterval - (DateTime.Now - last_refresh_time), RefreshResult.Success);
155                 }
156
157                 public virtual RegisterResponseInfo Register (RegisterInfo registerInfo)
158                 {
159                         if (registerInfo == null)
160                                 throw new ArgumentException ("Register info cannot be null.");
161                         
162                         if (! opened)
163                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
164                         
165                         return Register (registerInfo.ClientId, registerInfo.MeshId, registerInfo.NodeAddress);
166                 }
167
168                 public virtual RegisterResponseInfo Register (Guid clientId, string meshId, PeerNodeAddress address)
169                 {
170                         Node n = new Node () { RegistrationId = Guid.NewGuid (), MeshId = meshId, ClientId = clientId, NodeAddress = address };
171                         RegisterResponseInfo rri = new RegisterResponseInfo ();
172                         rri.RegistrationId = n.RegistrationId;
173                         lock (mesh_lock)
174                                 mesh.Add (n);
175                         
176                         return rri;
177                 }
178
179                 public virtual ResolveResponseInfo Resolve (ResolveInfo resolveInfo)
180                 {
181                         ResolveResponseInfo rri = new ResolveResponseInfo ();
182                         if (resolveInfo == null)
183                                 throw new ArgumentException ("Resolve info cannot be null.");
184                         
185                         if (! opened)
186                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
187                         
188                         foreach (var node in mesh)
189                                 if (node.MeshId == resolveInfo.MeshId &&
190                                     node.ClientId == resolveInfo.ClientId)
191                                         rri.Addresses.Add (node.NodeAddress);
192                         
193                         return rri;
194                 }
195
196                 public virtual void Unregister (UnregisterInfo unregisterInfo)
197                 {
198                         if (unregisterInfo == null)
199                                 throw new ArgumentException ("Unregister info cannot be null.");
200                         
201                         if (! opened)
202                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
203                         
204                         lock (mesh_lock)
205                                 foreach (var node in mesh)
206                                         if (node.MeshId == unregisterInfo.MeshId &&
207                                             node.RegistrationId == unregisterInfo.RegistrationId) {
208                                                 mesh.Remove (node);
209                                                 break;
210                                         }
211                 }
212
213                 [MonoTODO]
214                 public virtual RegisterResponseInfo Update (UpdateInfo updateInfo)
215                 {
216                         if (updateInfo == null)
217                                 throw new ArgumentException ("Update info cannot be null.");
218                         
219                         if (! opened)
220                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
221                         
222 //                      return new RegisterResponseInfo ();
223                         throw new NotImplementedException ();
224                 }
225         }
226         
227         internal class Node
228         {
229                 public Guid ClientId { get; set; }
230                 public string MeshId { get; set; }
231                 public Guid RegistrationId { get; set; }
232                 public PeerNodeAddress NodeAddress { get; set; }
233         }
234
235 #else
236         [MonoTODO ("Implement cleanup and refresh")]
237         // FIXME: TransactionTimeout must be null by-default.
238         [ServiceBehavior (AutomaticSessionShutdown = true, ConcurrencyMode = ConcurrencyMode.Multiple, 
239                           InstanceContextMode = InstanceContextMode.Single, ReleaseServiceInstanceOnTransactionComplete = true, 
240                           TransactionIsolationLevel = IsolationLevel.Unspecified, /*TransactionTimeout = null, */
241                           UseSynchronizationContext = false, ValidateMustUnderstand = true)]
242         public class CustomPeerResolverService : IPeerResolverContract
243         {
244                 static ServiceHost localhost;
245
246                 static void SetupCustomPeerResolverServiceHost ()
247                 {
248                         // launch peer resolver service locally only when it does not seem to be running ...
249                         var t = new TcpListener (8931);
250                         try {
251                                 t.Start ();
252                                 t.Stop ();
253                         } catch {
254                                 return;
255                         }
256                         Console.WriteLine ("WARNING: it is running peer resolver service locally. This means, the node registration is valid only within this application domain...");
257                         var host = new ServiceHost (new LocalPeerResolverService (TextWriter.Null));
258                         host.Description.Behaviors.Find<ServiceBehaviorAttribute> ().InstanceContextMode = InstanceContextMode.Single;
259                         host.AddServiceEndpoint (typeof (ICustomPeerResolverContract), new BasicHttpBinding (), "http://localhost:8931");
260                         localhost = host;
261                         host.Open ();
262                 }
263
264                 ICustomPeerResolverClient client;
265                 bool control_shape, opened;
266                 TimeSpan refresh_interval, cleanup_interval;
267
268                 public CustomPeerResolverService ()
269                 {
270                         client = ChannelFactory<ICustomPeerResolverClient>.CreateChannel (new BasicHttpBinding (), new EndpointAddress ("http://localhost:8931"));
271
272                         refresh_interval = new TimeSpan (0, 10, 0);
273                         cleanup_interval = new TimeSpan (0, 1, 0);
274                 }
275
276                 public TimeSpan CleanupInterval {
277                         get { return cleanup_interval; }
278                         set { 
279                                 if ((value < TimeSpan.Zero) || (value > TimeSpan.MaxValue))
280                                         throw new ArgumentOutOfRangeException (
281                                         "The interval is either zero or greater than max value.");
282                                 if (opened)
283                                         throw new InvalidOperationException ("The interval must be set before it is opened");
284
285                                 cleanup_interval = value;
286                         }
287                 }
288
289                 public bool ControlShape {
290                         get { return control_shape; }
291                         set {
292                                 if (opened)
293                                         throw new InvalidOperationException ("The interval must be set before it is opened");
294                                 control_shape = value;
295                         }
296                 }
297
298                 public TimeSpan RefreshInterval {
299                         get { return refresh_interval; }
300                         set {
301                                 if ((value < TimeSpan.Zero) || (value > TimeSpan.MaxValue))
302                                         throw new ArgumentOutOfRangeException (
303                                         "The interval is either zero or greater than max value.");
304                                 if (opened)
305                                         throw new InvalidOperationException ("The interval must be set before it is opened");
306
307                                 refresh_interval = value;
308                         }
309                 }
310
311                 [MonoTODO ("Do we have to unregister nodes here?")]
312                 public virtual void Close ()
313                 {
314                         if (! opened)
315                                 throw new InvalidOperationException ("The service has never been opened or it was closed by a previous call to this method.");
316                         client.Close ();
317                         opened = false;
318
319                         if (localhost != null) {
320                                 localhost.Close ();
321                                 localhost = null;
322                         }
323                 }
324
325                 public virtual ServiceSettingsResponseInfo GetServiceSettings ()
326                 {
327                         if (! opened)
328                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
329                 
330                         return client.GetServiceSettings ();
331                 }
332
333                 public virtual void Open ()
334                 {
335                         if (localhost == null)
336                                 SetupCustomPeerResolverServiceHost ();
337
338                         if ((CleanupInterval == TimeSpan.Zero) || (RefreshInterval == TimeSpan.Zero))
339                                 throw new ArgumentException ("Cleanup interval or refresh interval are set to a time span interval of zero.");
340
341                         if (opened)
342                                 throw new InvalidOperationException ("The service has been started by a previous call to this method.");
343                         
344                         opened = true;
345
346                         client.Open ();
347                         client.SetCustomServiceSettings (new PeerServiceSettingsInfo () { ControlMeshShape = control_shape, RefreshInterval = refresh_interval, CleanupInterval = cleanup_interval });
348                 }
349
350                 public virtual RefreshResponseInfo Refresh (RefreshInfo refreshInfo)
351                 {
352                         if (refreshInfo == null)
353                                 throw new ArgumentException ("Refresh info cannot be null.");
354                         
355                         if (! opened)
356                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
357
358                         return client.Refresh (refreshInfo);
359                 }
360
361                 public virtual RegisterResponseInfo Register (RegisterInfo registerInfo)
362                 {
363                         if (registerInfo == null)
364                                 throw new ArgumentException ("Register info cannot be null.");
365                         
366                         if (! opened)
367                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
368                         
369                         return client.Register (registerInfo);
370                 }
371
372                 public virtual RegisterResponseInfo Register (Guid clientId, string meshId, PeerNodeAddress address)
373                 {
374                         return Register (new RegisterInfo (clientId, meshId, address));
375                 }
376
377                 public virtual ResolveResponseInfo Resolve (ResolveInfo resolveInfo)
378                 {
379                         if (resolveInfo == null)
380                                 throw new ArgumentException ("Resolve info cannot be null.");
381                         
382                         if (! opened)
383                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
384
385                         return client.Resolve (resolveInfo);
386                 }
387
388                 public virtual void Unregister (UnregisterInfo unregisterInfo)
389                 {
390                         if (unregisterInfo == null)
391                                 throw new ArgumentException ("Unregister info cannot be null.");
392                         
393                         if (! opened)
394                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
395
396                         client.Unregister (unregisterInfo);
397                 }
398
399                 public virtual RegisterResponseInfo Update (UpdateInfo updateInfo)
400                 {
401                         if (updateInfo == null)
402                                 throw new ArgumentException ("Update info cannot be null.");
403                         
404                         if (! opened)
405                                 throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
406
407                         return client.Update (updateInfo);
408                 }
409         }
410 #endif
411 }