2009-07-07 Atsushi Enomoto <atsushi@ximian.com>
authorAtsushi Eno <atsushieno@gmail.com>
Tue, 7 Jul 2009 15:16:55 +0000 (15:16 -0000)
committerAtsushi Eno <atsushieno@gmail.com>
Tue, 7 Jul 2009 15:16:55 +0000 (15:16 -0000)
* CustomPeerResolverService.cs, LocalPeerResolverService.cs,
  ICustomPeerResolverContract.cs : add local machine peer resolver
  implementation that can work across appdomains (based on
  dispatching peer resolver that communicates with a ServiceHost
  which is either locally started or running as a different process).

* System.ServiceModel.dll.sources:
  Added new custom peer resolver files.

svn path=/trunk/mcs/; revision=137504

mcs/class/System.ServiceModel/ChangeLog
mcs/class/System.ServiceModel/System.ServiceModel.PeerResolvers/ChangeLog
mcs/class/System.ServiceModel/System.ServiceModel.PeerResolvers/CustomPeerResolverService.cs
mcs/class/System.ServiceModel/System.ServiceModel.PeerResolvers/ICustomPeerResolverContract.cs [new file with mode: 0755]
mcs/class/System.ServiceModel/System.ServiceModel.PeerResolvers/LocalPeerResolverService.cs [new file with mode: 0755]
mcs/class/System.ServiceModel/System.ServiceModel.dll.sources

index c9df180d10cedd6feec9566848abe28af828e3e1..d9b764e5985dd0d98b3c5e646296ec0b35a5c9bc 100755 (executable)
@@ -1,3 +1,8 @@
+2009-07-07  Astushi Enomoto  <atsushi@ximian.com>
+
+       * System.ServiceModel.dll.sources:
+         Added new custom peer resolver files.
+
 2009-07-07  Astushi Enomoto  <atsushi@ximian.com>
 
        * System.ServiceModel_test.dll.sources:
index e7f12bda331c8f4e4ac2e25a42ce98440518e21e..e13e28970aa5369a9e515d349d08a7ad6687f338 100644 (file)
@@ -1,3 +1,11 @@
+2009-07-07  Atsushi Enomoto  <atsushi@ximian.com>
+
+       * CustomPeerResolverService.cs, LocalPeerResolverService.cs,
+         ICustomPeerResolverContract.cs : add local machine peer resolver
+         implementation that can work across appdomains (based on
+         dispatching peer resolver that communicates with a ServiceHost
+         which is either locally started or running as a different process).
+
 2009-06-01  Atsushi Enomoto  <atsushi@ximian.com>
 
        * CustomPeerResolverService.cs : Refresh() now gives readable reply.
index ce9e772d784193e97aefa7451024e307cfdcb814..a1856d1bc88d1254d1f0f095cde866d1c8ee3d8d 100644 (file)
 //
 
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
+using System.Net.Sockets;
 using System.Transactions;
 using System.Timers;
 
 namespace System.ServiceModel.PeerResolvers
 {
+#if false
        [MonoTODO ("Implement cleanup and refresh")]
        // FIXME: TransactionTimeout must be null by-default.
        [ServiceBehavior (AutomaticSessionShutdown = true, ConcurrencyMode = ConcurrencyMode.Multiple, 
@@ -228,4 +231,181 @@ namespace System.ServiceModel.PeerResolvers
                public Guid RegistrationId { get; set; }
                public PeerNodeAddress NodeAddress { get; set; }
        }
+
+#else
+       [MonoTODO ("Implement cleanup and refresh")]
+       // FIXME: TransactionTimeout must be null by-default.
+       [ServiceBehavior (AutomaticSessionShutdown = true, ConcurrencyMode = ConcurrencyMode.Multiple, 
+                         InstanceContextMode = InstanceContextMode.Single, ReleaseServiceInstanceOnTransactionComplete = true, 
+                         TransactionIsolationLevel = IsolationLevel.Unspecified, /*TransactionTimeout = null, */
+                         UseSynchronizationContext = false, ValidateMustUnderstand = true)]
+       public class CustomPeerResolverService : IPeerResolverContract
+       {
+               static ServiceHost localhost;
+
+               static void SetupCustomPeerResolverServiceHost ()
+               {
+                       // launch peer resolver service locally only when it does not seem to be running ...
+                       var t = new TcpListener (8931);
+                       try {
+                               t.Start ();
+                               t.Stop ();
+                       } catch {
+                               return;
+                       }
+                       Console.WriteLine ("WARNING: it is running peer resolver service locally. This means, the node registration is valid only within this application domain...");
+                       var host = new ServiceHost (new LocalPeerResolverService (TextWriter.Null));
+                       host.Description.Behaviors.Find<ServiceBehaviorAttribute> ().InstanceContextMode = InstanceContextMode.Single;
+                       host.AddServiceEndpoint (typeof (ICustomPeerResolverContract), new BasicHttpBinding (), "http://localhost:8931");
+                       localhost = host;
+                       host.Open ();
+               }
+
+               ICustomPeerResolverClient client;
+               bool control_shape, opened;
+               TimeSpan refresh_interval, cleanup_interval;
+
+               public CustomPeerResolverService ()
+               {
+                       client = ChannelFactory<ICustomPeerResolverClient>.CreateChannel (new BasicHttpBinding (), new EndpointAddress ("http://localhost:8931"));
+
+                       refresh_interval = new TimeSpan (0, 10, 0);
+                       cleanup_interval = new TimeSpan (0, 1, 0);
+               }
+
+               public TimeSpan CleanupInterval {
+                       get { return cleanup_interval; }
+                       set { 
+                               if ((value < TimeSpan.Zero) || (value > TimeSpan.MaxValue))
+                                       throw new ArgumentOutOfRangeException (
+                                       "The interval is either zero or greater than max value.");
+                               if (opened)
+                                       throw new InvalidOperationException ("The interval must be set before it is opened");
+
+                               cleanup_interval = value;
+                       }
+               }
+
+               public bool ControlShape {
+                       get { return control_shape; }
+                       set {
+                               if (opened)
+                                       throw new InvalidOperationException ("The interval must be set before it is opened");
+                               control_shape = value;
+                       }
+               }
+
+               public TimeSpan RefreshInterval {
+                       get { return refresh_interval; }
+                       set {
+                               if ((value < TimeSpan.Zero) || (value > TimeSpan.MaxValue))
+                                       throw new ArgumentOutOfRangeException (
+                                       "The interval is either zero or greater than max value.");
+                               if (opened)
+                                       throw new InvalidOperationException ("The interval must be set before it is opened");
+
+                               refresh_interval = value;
+                       }
+               }
+
+               [MonoTODO ("Do we have to unregister nodes here?")]
+               public virtual void Close ()
+               {
+                       if (! opened)
+                               throw new InvalidOperationException ("The service has never been opened or it was closed by a previous call to this method.");
+                       client.Close ();
+                       opened = false;
+
+                       if (localhost != null) {
+                               localhost.Close ();
+                               localhost = null;
+                       }
+               }
+
+               public virtual ServiceSettingsResponseInfo GetServiceSettings ()
+               {
+                       if (! opened)
+                               throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
+               
+                       return client.GetServiceSettings ();
+               }
+
+               public virtual void Open ()
+               {
+                       if (localhost == null)
+                               SetupCustomPeerResolverServiceHost ();
+
+                       if ((CleanupInterval == TimeSpan.Zero) || (RefreshInterval == TimeSpan.Zero))
+                               throw new ArgumentException ("Cleanup interval or refresh interval are set to a time span interval of zero.");
+
+                       if (opened)
+                               throw new InvalidOperationException ("The service has been started by a previous call to this method.");
+                       
+                       opened = true;
+
+                       client.Open ();
+                       client.SetCustomServiceSettings (new PeerServiceSettingsInfo () { ControlMeshShape = control_shape, RefreshInterval = refresh_interval, CleanupInterval = cleanup_interval });
+               }
+
+               public virtual RefreshResponseInfo Refresh (RefreshInfo refreshInfo)
+               {
+                       if (refreshInfo == null)
+                               throw new ArgumentException ("Refresh info cannot be null.");
+                       
+                       if (! opened)
+                               throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
+
+                       return client.Refresh (refreshInfo);
+               }
+
+               public virtual RegisterResponseInfo Register (RegisterInfo registerInfo)
+               {
+                       if (registerInfo == null)
+                               throw new ArgumentException ("Register info cannot be null.");
+                       
+                       if (! opened)
+                               throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
+                       
+                       return client.Register (registerInfo);
+               }
+
+               public virtual RegisterResponseInfo Register (Guid clientId, string meshId, PeerNodeAddress address)
+               {
+                       return Register (new RegisterInfo (clientId, meshId, address));
+               }
+
+               public virtual ResolveResponseInfo Resolve (ResolveInfo resolveInfo)
+               {
+                       if (resolveInfo == null)
+                               throw new ArgumentException ("Resolve info cannot be null.");
+                       
+                       if (! opened)
+                               throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
+
+                       return client.Resolve (resolveInfo);
+               }
+
+               public virtual void Unregister (UnregisterInfo unregisterInfo)
+               {
+                       if (unregisterInfo == null)
+                               throw new ArgumentException ("Unregister info cannot be null.");
+                       
+                       if (! opened)
+                               throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
+
+                       client.Unregister (unregisterInfo);
+               }
+
+               public virtual RegisterResponseInfo Update (UpdateInfo updateInfo)
+               {
+                       if (updateInfo == null)
+                               throw new ArgumentException ("Update info cannot be null.");
+                       
+                       if (! opened)
+                               throw new InvalidOperationException ("The service has never been opened or it was closed previously.");
+
+                       return client.Update (updateInfo);
+               }
+       }
+#endif
 }
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.PeerResolvers/ICustomPeerResolverContract.cs b/mcs/class/System.ServiceModel/System.ServiceModel.PeerResolvers/ICustomPeerResolverContract.cs
new file mode 100755 (executable)
index 0000000..c3b7c27
--- /dev/null
@@ -0,0 +1,32 @@
+using System;
+using System.Runtime.Serialization;
+using System.ServiceModel;
+using System.ServiceModel.PeerResolvers;
+using System.ServiceModel.Description;
+
+namespace System.ServiceModel.PeerResolvers
+{
+       [DataContract (Namespace = "http://mono-project.com/ns/2008/07/peer-resolver")]
+       class PeerServiceSettingsInfo
+       {
+               [DataMember]
+               public TimeSpan RefreshInterval { get; set; }
+               [DataMember]
+               public TimeSpan CleanupInterval { get; set; }
+               [DataMember]
+               public bool ControlMeshShape { get; set; }
+       }
+
+       [ServiceContract]
+       interface ICustomPeerResolverContract : IPeerResolverContract
+       {
+               [OperationContract]
+               PeerServiceSettingsInfo GetCustomServiceSettings ();
+               [OperationContract]
+               void SetCustomServiceSettings (PeerServiceSettingsInfo info);
+       }
+
+       interface ICustomPeerResolverClient : ICustomPeerResolverContract, IClientChannel
+       {
+       }
+}
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.PeerResolvers/LocalPeerResolverService.cs b/mcs/class/System.ServiceModel/System.ServiceModel.PeerResolvers/LocalPeerResolverService.cs
new file mode 100755 (executable)
index 0000000..236ec43
--- /dev/null
@@ -0,0 +1,212 @@
+// 
+// LocalPeerResolverService.cs
+// 
+// Author: 
+//     Atsushi Enomoto  <atsushi@ximian.com>
+// 
+// Copyright (C) 2009 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if true
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.ServiceModel;
+using System.ServiceModel.PeerResolvers;
+
+namespace System.ServiceModel.PeerResolvers
+{
+       // This implementation of peer resolver should open up node
+       // registration to some extent, say, valid within the same machine.
+       //
+       // A correct implementation should be using something like zeroconf
+       // to register the local machine as a peer node.
+
+       class LocalPeerResolverService : ICustomPeerResolverContract
+       {
+               public LocalPeerResolverService (TextWriter log)
+               {
+                       this.log = log ?? TextWriter.Null;
+               }
+
+               TextWriter log;
+               Dictionary<string,Mesh> mesh_map = new Dictionary<string,Mesh> ();
+
+               // CustomPeerResolverService delegation
+
+               // Open(), Close(), ControlShape, RefreshInterval, CleanupInterval
+
+               public bool ControlShape { get; set; }
+               public TimeSpan RefreshInterval { get; set; }
+               public TimeSpan CleanupInterval { get; set; }
+
+               // (internal) ICustomPeerResolverContract implementation
+
+               public PeerServiceSettingsInfo GetCustomServiceSettings ()
+               {
+                       log.WriteLine ("REQUEST: GetCustomServiceSettings");
+                       return new PeerServiceSettingsInfo () {
+                               ControlMeshShape = this.ControlShape,
+                               RefreshInterval = this.RefreshInterval, 
+                               CleanupInterval = this.CleanupInterval };
+               }
+
+               public void SetCustomServiceSettings (PeerServiceSettingsInfo info)
+               {
+                       log.WriteLine ("REQUEST: SetCustomServiceSettings(ControlMeshShape:{0}, RefreshInterval:{1}, CleanupInterval:{2}", info.ControlMeshShape, info.RefreshInterval, info.CleanupInterval);
+                       ControlShape = info.ControlMeshShape;
+                       RefreshInterval = info.RefreshInterval;
+                       CleanupInterval = info.CleanupInterval;
+               }
+
+               // IPeerResolverContract implementation
+
+               public ServiceSettingsResponseInfo GetServiceSettings ()
+               {
+                       return new ServiceSettingsResponseInfo () { ControlMeshShape = this.ControlShape };
+               }
+
+               public RefreshResponseInfo Refresh (RefreshInfo refreshInfo)
+               {
+                       var r = refreshInfo;
+                       log.WriteLine ("REQUEST: Refresh (Mesh: {0}, Registraion: {1})", r.MeshId, r.RegistrationId);
+                       var mesh = GetExistingMesh (r.MeshId);
+                       var node = mesh.FirstOrDefault (n => n.RegistrationId == r.RegistrationId);
+                       if (node == null)
+                               return new RefreshResponseInfo () { Result = RefreshResult.RegistrationNotFound };
+                       node.Refresh ();
+                       return new RefreshResponseInfo () { Result = RefreshResult.Success, RegistrationLifetime = DateTime.Now - node.LastRefreshTime };
+               }
+
+               public RegisterResponseInfo Register (RegisterInfo registerInfo)
+               {
+                       var r = registerInfo;
+                       log.WriteLine ("REQUEST: Register (Mesh: {0}, Client: {1}, NodeAddress: endpoint {2})", r.MeshId, r.ClientId, r.NodeAddress.EndpointAddress);
+                       Mesh mesh;
+                       if (!mesh_map.TryGetValue (r.MeshId, out mesh)) {
+                               mesh = new Mesh (r.MeshId);
+                               mesh_map.Add (r.MeshId, mesh);
+                       }
+                       var node = RegisterNode (mesh, r.ClientId, r.NodeAddress);
+                       return new RegisterResponseInfo () { RegistrationId = node.RegistrationId };
+               }
+
+               public ResolveResponseInfo Resolve (ResolveInfo resolveInfo)
+               {
+                       var r = resolveInfo;
+                       log.WriteLine ("REQUEST: Resolve (Mesh: {0}, Client: {1}, MaxAddresses: {2})", r.MeshId, r.ClientId, r.MaxAddresses);
+                       Mesh mesh;
+                       var rr = new ResolveResponseInfo ();
+                       if (!mesh_map.TryGetValue (r.MeshId, out mesh))
+                               return rr;
+                       foreach (var node in mesh.TakeWhile (n => n.ClientId == r.ClientId)) {
+                               rr.Addresses.Add (node.Address);
+                               if (rr.Addresses.Count == r.MaxAddresses)
+                                       break;
+                       }
+                       return rr;
+               }
+
+               public void Unregister (UnregisterInfo unregisterInfo)
+               {
+                       var u = unregisterInfo;
+                       log.WriteLine ("REQUEST: Unregister (Mesh: {0}, Registration: {1})", u.MeshId, u.RegistrationId);
+                       Mesh mesh = GetExistingMesh (u.MeshId);
+                       lock (mesh) {
+                               var node = mesh.GetRegisteredNode (u.RegistrationId);
+                               mesh.Remove (node);
+                       }
+               }
+
+               public RegisterResponseInfo Update (UpdateInfo updateInfo)
+               {
+                       var u = updateInfo;
+                       log.WriteLine ("REQUEST: Update (Mesh: {0}, Registration: {1}, NodeAddress:)", u.MeshId, u.RegistrationId, u.NodeAddress);
+                       var mesh = GetExistingMesh (u.MeshId);
+                       var node = mesh.GetRegisteredNode (u.RegistrationId);
+                       node.Update (u.NodeAddress);
+                       return new RegisterResponseInfo () { RegistrationId = node.RegistrationId };
+               }
+
+               Mesh GetExistingMesh (string meshId)
+               {
+                       Mesh mesh;
+                       if (!mesh_map.TryGetValue (meshId, out mesh))
+                               throw new InvalidOperationException (String.Format ("Specified mesh {0} does not exist", meshId));
+                       return mesh;
+               }
+
+               Node RegisterNode (Mesh mesh, Guid clientId, PeerNodeAddress addr)
+               {
+                       lock (mesh) {
+                               var node = new Node () { ClientId = clientId, Address = addr };
+                               mesh.Add (node);
+                               node.LastRefreshTime = DateTime.Now;
+                               return node;
+                       }
+               }
+       }
+
+       class Mesh : List<Node>
+       {
+               public Mesh (string id)
+               {
+                       Id = id;
+               }
+
+               public string Id { get; private set; }
+
+               public Node GetRegisteredNode (Guid registrationId)
+               {
+                       var node = this.FirstOrDefault (n => n.RegistrationId == registrationId);
+                       if (node == null)
+                               throw new InvalidOperationException (String.Format ("Node with registration Id {0} does not exist in the specified mesh {0}", registrationId, Id));
+                       return node;
+               }
+       }
+
+       class Node
+       {
+               public Node ()
+               {
+                       RegistrationId = Guid.NewGuid ();
+               }
+
+               public Guid RegistrationId { get; private set; }
+               public Guid ClientId { get; set; }
+               public PeerNodeAddress Address { get; set; }
+               public DateTime LastRefreshTime { get; set; }
+
+               public void Refresh ()
+               {
+                       LastRefreshTime = DateTime.Now;
+               }
+
+               public void Update (PeerNodeAddress addr)
+               {
+                       Address = addr;
+                       LastRefreshTime = DateTime.Now;
+               }
+       }
+}
+#endif
index 34191eea5ec2b7be8aacec157f379978321369de..2a41e2c0cc87fb0d864cb29bfe5fbb4d7b89aee6 100755 (executable)
@@ -671,7 +671,9 @@ System.ServiceModel.MsmqIntegration/MsmqIntegrationMessageProperty.cs
 System.ServiceModel.MsmqIntegration/MsmqIntegrationSecurity.cs
 System.ServiceModel.MsmqIntegration/MsmqMessage.cs
 System.ServiceModel.PeerResolvers/CustomPeerResolverService.cs
+System.ServiceModel.PeerResolvers/ICustomPeerResolverContract.cs
 System.ServiceModel.PeerResolvers/IPeerResolverContract.cs
+System.ServiceModel.PeerResolvers/LocalPeerResolverService.cs
 System.ServiceModel.PeerResolvers/PeerCustomResolverSettings.cs
 System.ServiceModel.PeerResolvers/PeerReferralPolicy.cs
 System.ServiceModel.PeerResolvers/PeerResolverMode.cs