2009-08-24 Atsushi Enomoto <atsushi@ximian.com>
authorAtsushi Eno <atsushieno@gmail.com>
Mon, 24 Aug 2009 08:57:57 +0000 (08:57 -0000)
committerAtsushi Eno <atsushieno@gmail.com>
Mon, 24 Aug 2009 08:57:57 +0000 (08:57 -0000)
* ClientRuntimeChannel.cs, DuplexClientRuntimeChannel.cs : split out
  from former to latter and implemented most part of listener part.
  Make sure to open internal channel at base ClientRuntimeChannel.
* ClientProxyGenerator.cs : minimize !NET_2_1 to here.

* System.ServiceModel.dll.sources: added
  DuplexClientRuntimeChannel.cs and CallbackInstanceContextProvider.cs.

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

mcs/class/System.ServiceModel/ChangeLog
mcs/class/System.ServiceModel/System.ServiceModel.dll.sources
mcs/class/System.ServiceModel/System.ServiceModel/ChangeLog
mcs/class/System.ServiceModel/System.ServiceModel/ClientProxyGenerator.cs
mcs/class/System.ServiceModel/System.ServiceModel/ClientRuntimeChannel.cs
mcs/class/System.ServiceModel/System.ServiceModel/DuplexClientRuntimeChannel.cs [new file with mode: 0644]

index 505544ee87b47a3099441ea803fd2f6504acfb42..e441d4b405bff62a514cbcbce50f28804e156ea8 100755 (executable)
@@ -1,3 +1,8 @@
+2009-08-24  Astushi Enomoto  <atsushi@ximian.com>
+
+       * System.ServiceModel.dll.sources: added 
+         DuplexClientRuntimeChannel.cs and CallbackInstanceContextProvider.cs.
+
 2009-08-20  Astushi Enomoto  <atsushi@ximian.com>
 
        * System.ServiceModel.dll.sources: add more named pipe files.
index b81ae37f9ccb0cc2391b0ec70976e2780003f215..5da0d9909f401890d4a6bc6c1daedc152be67cbf 100755 (executable)
@@ -611,6 +611,7 @@ System.ServiceModel.Dispatcher/ActionMessageFilterTable.cs
 System.ServiceModel.Dispatcher/BaseMessagesFormatter.cs
 System.ServiceModel.Dispatcher/BaseRequestProcessor.cs
 System.ServiceModel.Dispatcher/BaseRequestProcessorHandler.cs
+System.ServiceModel.Dispatcher/CallbackInstanceContextProvider.cs
 System.ServiceModel.Dispatcher/ChannelDispatcher.cs
 System.ServiceModel.Dispatcher/ChannelDispatcherBase.cs
 System.ServiceModel.Dispatcher/ChannelDispatcherCollection.cs
@@ -813,6 +814,7 @@ System.ServiceModel/DnsEndpointIdentity.cs
 System.ServiceModel/Dummy.cs
 System.ServiceModel/DuplexChannelFactory.cs
 System.ServiceModel/DuplexClientBase.cs
+System.ServiceModel/DuplexClientRuntimeChannel.cs
 System.ServiceModel/EndpointAddress.cs
 System.ServiceModel/EndpointAddress10.cs
 System.ServiceModel/EndpointAddressAugust2004.cs
index 389c9014a4f94957079e104d9bcbb67cf6528391..944e0622de2689c6c0e0eddde18b3c61b83145e7 100755 (executable)
@@ -1,3 +1,10 @@
+2009-08-24  Atsushi Enomoto  <atsushi@ximian.com>
+
+       * ClientRuntimeChannel.cs, DuplexClientRuntimeChannel.cs : split out
+         from former to latter and implemented most part of listener part.
+         Make sure to open internal channel at base ClientRuntimeChannel.
+       * ClientProxyGenerator.cs : minimize !NET_2_1 to here.
+
 2009-08-21  Atsushi Enomoto  <atsushi@ximian.com>
 
        * ServiceHostBase.cs : some refactoring to reduce weird limitation
index d5afac9f95f9a6a9f54ca0dcd4610de33181edb7..36fbd306cdac7899f83289291500d3926d5d5e2c 100644 (file)
@@ -41,7 +41,11 @@ namespace System.ServiceModel
                public static Type CreateProxyType (Type contractInterface, ContractDescription cd, bool duplex)
                {
                        string modname = "dummy";
-                       Type crtype = duplex ? typeof (DuplexClientRuntimeChannel) : typeof (ClientRuntimeChannel);
+                       Type crtype =
+#if !NET_2_1
+                               duplex ? typeof (DuplexClientRuntimeChannel) :
+#endif
+                               typeof (ClientRuntimeChannel);
 
                        // public class __clientproxy_MyContract : ClientRuntimeChannel, [ContractType]
                        CodeClass c = new CodeModule (modname).CreateClass (
index 3ef8b8ba93d71783ea0ca397586a8ca06bdff0ef..7a66a05f9901cab8f97cb9254a7bc4a279be7e37 100644 (file)
@@ -36,56 +36,6 @@ using System.Xml;
 
 namespace System.ServiceModel
 {
-#if NET_2_1
-       internal class DuplexClientRuntimeChannel
-       {
-       }
-#else
-       internal class DuplexClientRuntimeChannel
-               : ClientRuntimeChannel, IDuplexContextChannel
-       {
-               public DuplexClientRuntimeChannel (ServiceEndpoint endpoint,
-                       ChannelFactory factory, EndpointAddress remoteAddress, Uri via)
-                       : base (endpoint, factory, remoteAddress, via)
-               {
-               }
-
-               public bool AutomaticInputSessionShutdown {
-                       get { throw new NotImplementedException (); }
-                       set { throw new NotImplementedException (); }
-               }
-
-               public InstanceContext CallbackInstance { get; set; }
-
-               Action<TimeSpan> session_shutdown_delegate;
-
-               public void CloseOutputSession (TimeSpan timeout)
-               {
-                       throw new NotImplementedException ();
-               }
-
-               public IAsyncResult BeginCloseOutputSession (TimeSpan timeout, AsyncCallback callback, object state)
-               {
-                       if (session_shutdown_delegate == null)
-                               session_shutdown_delegate = new Action<TimeSpan> (CloseOutputSession);
-                       return session_shutdown_delegate.BeginInvoke (timeout, callback, state);
-               }
-
-               public void EndCloseOutputSession (IAsyncResult result)
-               {
-                       session_shutdown_delegate.EndInvoke (result);
-               }
-
-               // base channel overrides.
-
-               protected override void OnOpen (TimeSpan timeout)
-               {
-                       // FIXME: add callback listener here to receive callbacks.
-                       base.OnOpen (timeout);
-               }
-       }
-#endif
-
        internal class ClientRuntimeChannel
                : CommunicationObject, IClientChannel
        {
@@ -155,6 +105,10 @@ namespace System.ServiceModel
                        get { return channel as IOutputChannel; }
                }
 
+               internal IDuplexChannel DuplexChannel {
+                       get { return channel as IDuplexChannel; }
+               }
+
                #region IClientChannel
 
                bool did_interactive_initialization;
@@ -413,6 +367,8 @@ namespace System.ServiceModel
                {
                        if (runtime.InteractiveChannelInitializers.Count > 0 && !DidInteractiveInitialization)
                                throw new InvalidOperationException ("The client runtime is assigned interactive channel initializers, and in such case DisplayInitializationUI must be called before the channel is opened.");
+                       if (channel.State == CommunicationState.Created)
+                               channel.Open (timeout);
                }
 
                // IChannel
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel/DuplexClientRuntimeChannel.cs b/mcs/class/System.ServiceModel/System.ServiceModel/DuplexClientRuntimeChannel.cs
new file mode 100644 (file)
index 0000000..32bb5fc
--- /dev/null
@@ -0,0 +1,179 @@
+//
+// DuplexClientRuntimeChannel.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.
+//
+using System;
+using System.Reflection;
+using System.ServiceModel.Channels;
+using System.ServiceModel.Description;
+using System.ServiceModel.Dispatcher;
+using System.ServiceModel.Security;
+using System.Threading;
+using System.Xml;
+
+namespace System.ServiceModel
+{
+       internal class DuplexClientRuntimeChannel
+               : ClientRuntimeChannel, IDuplexContextChannel
+       {
+               public DuplexClientRuntimeChannel (ServiceEndpoint endpoint,
+                       ChannelFactory factory, EndpointAddress remoteAddress, Uri via)
+                       : base (endpoint, factory, remoteAddress, via)
+               {
+                       var cd = ContractDescription.GetContract (endpoint.Contract.CallbackContractType);
+                       var se = new ServiceEndpoint (cd, factory.Endpoint.Binding, remoteAddress);
+                       var ed = new EndpointDispatcher (remoteAddress, cd.Name, cd.Namespace);
+                       ed.InitializeServiceEndpoint (true, null, null, se);
+                       Runtime.CallbackDispatchRuntime = ed.DispatchRuntime;
+               }
+
+               public bool AutomaticInputSessionShutdown {
+                       get { throw new NotImplementedException (); }
+                       set { throw new NotImplementedException (); }
+               }
+
+               InstanceContext callback_instance;
+
+               public InstanceContext CallbackInstance {
+                       get { return callback_instance; }
+                       set {
+                               callback_instance = value;
+                               Runtime.CallbackDispatchRuntime.InstanceContextProvider = new CallbackInstanceContextProvider (callback_instance);
+                       }
+               }
+
+               Action<TimeSpan> session_shutdown_delegate;
+
+               public void CloseOutputSession (TimeSpan timeout)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               public IAsyncResult BeginCloseOutputSession (TimeSpan timeout, AsyncCallback callback, object state)
+               {
+                       if (session_shutdown_delegate == null)
+                               session_shutdown_delegate = new Action<TimeSpan> (CloseOutputSession);
+                       return session_shutdown_delegate.BeginInvoke (timeout, callback, state);
+               }
+
+               public void EndCloseOutputSession (IAsyncResult result)
+               {
+                       session_shutdown_delegate.EndInvoke (result);
+               }
+
+               // listener loop manager
+
+               bool loop;
+
+               TimeSpan receive_timeout;
+               bool receive_synchronously = true; // FIXME
+
+               IAsyncResult loop_result;
+               AutoResetEvent loop_handle = new AutoResetEvent (false);
+               AutoResetEvent finish_handle = new AutoResetEvent (false);
+
+               protected override void OnOpen (TimeSpan timeout)
+               {
+                       loop = true;
+                       base.OnOpen (timeout);
+                       receive_timeout = TimeSpan.FromSeconds (10);
+               }
+
+               protected override void OnOpened ()
+               {
+                       base.OnOpened ();
+                       loop_result = new Action<IInputChannel> (ProcessRequestOrInput).BeginInvoke (DuplexChannel, null, null);
+               }
+
+               protected override void OnClose (TimeSpan timeout)
+               {
+                       DateTime start = DateTime.Now;
+                       base.OnClose (timeout);
+                       loop = false;
+                       if (!loop_handle.WaitOne (timeout - (DateTime.Now - start)))
+                               throw new TimeoutException ();
+                       if (!finish_handle.WaitOne (timeout - (DateTime.Now - start)))
+                               throw new TimeoutException ();
+               }
+
+               void ProcessRequestOrInput (IInputChannel input)
+               {
+                       while (true) {
+                               if (!loop)
+                                       return;
+
+                               if (receive_synchronously) {
+                                       Message msg;
+                                       if (input.TryReceive (receive_timeout, out msg))
+                                               ProcessInput (input, msg);
+                               } else {
+                                       input.BeginTryReceive (receive_timeout, TryReceiveDone, input);
+                                       loop_handle.WaitOne (receive_timeout);
+                               }
+                       }
+               }
+
+               void TryReceiveDone (IAsyncResult result)
+               {
+                       try {
+                               Message msg;
+                               var input = (IInputChannel) result.AsyncState;
+                               if (input.EndTryReceive (result, out msg)) {
+                                       loop_handle.Set ();
+                                       ProcessInput (input, msg);
+                               }
+                       } catch (Exception ex) {
+                               // FIXME: rather log it
+                               Console.WriteLine ("Error at duplex client receiver side");
+                               Console.WriteLine (ex);
+                               loop = false;
+                       }
+               }
+
+               void ProcessInput (IInputChannel input, Message message)
+               {
+                       try {
+                               if (!MessageMatchesEndpointDispatcher (message, Runtime.CallbackDispatchRuntime.EndpointDispatcher))
+                                       throw new EndpointNotFoundException (String.Format ("The request message has the target '{0}' with action '{1}' which is not reachable in this service contract", message.Headers.To, message.Headers.Action));
+                               new InputOrReplyRequestProcessor (Runtime.CallbackDispatchRuntime, input).ProcessInput (message);
+                       } catch (Exception ex) {
+                               // FIXME: log it.
+                               Console.WriteLine (ex);
+                       } finally {
+                               // unless it is closed by session/call manager, move it back to the loop to receive the next message.
+                               if (input.State != CommunicationState.Closed)
+                                       ProcessRequestOrInput (input);
+                       }
+               }
+
+               bool MessageMatchesEndpointDispatcher (Message req, EndpointDispatcher endpoint)
+               {
+                       // FIXME: no need to filter address? It'd be mostly anonymous URI though.
+
+                       return endpoint.ContractFilter.Match (req);
+               }
+       }
+}