New tests.
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ClientBase.cs
index f7ffe5198a06341c2bc519908016befa8982a222..d9e98a1856700aeeaee3c63f3cc801deaa1a8686 100644 (file)
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Reflection;
 using System.ServiceModel.Channels;
 using System.ServiceModel.Description;
 using System.ServiceModel.Dispatcher;
 using System.Threading;
+using System.ServiceModel.MonoInternal;
 
 namespace System.ServiceModel
 {
-       [MonoTODO ("It somehow rejects classes, but dunno how we can do that besides our code level.")]
-       public abstract class ClientBase<TChannel>
-               : IDisposable, ICommunicationObject
+       [MonoTODO ("It somehow rejects classes, but dunno how we can do that besides our code wise.")]
+       public abstract class ClientBase<TChannel> :
+#if !MOONLIGHT
+               IDisposable,
+#endif
+               ICommunicationObject where TChannel : class
        {
                static InstanceContext initialContxt = new InstanceContext (null);
+#if MOONLIGHT
+               static readonly PropertyInfo dispatcher_main_property;
+               static readonly MethodInfo dispatcher_begin_invoke_method;
+
+               static ClientBase ()
+               {
+                       Type dispatcher_type = Type.GetType ("System.Windows.Threading.Dispatcher, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", true);
+
+                       dispatcher_main_property = dispatcher_type.GetProperty ("Main", BindingFlags.NonPublic | BindingFlags.Static);
+                       if (dispatcher_main_property == null)
+                               throw new SystemException ("Dispatcher.Main not found");
+
+                       dispatcher_begin_invoke_method = dispatcher_type.GetMethod ("BeginInvoke", new Type [] {typeof (Delegate), typeof (object [])});
+                       if (dispatcher_begin_invoke_method == null)
+                               throw new SystemException ("Dispatcher.BeginInvoke not found");
+               }
+#endif
 
                ChannelFactory<TChannel> factory;
-               ClientRuntimeChannel inner_channel;
+               IClientChannel inner_channel;
                CommunicationState state;
 
                protected delegate IAsyncResult BeginOperationDelegate (object[] inValues, AsyncCallback asyncCallback, object state);
@@ -127,27 +149,34 @@ namespace System.ServiceModel
                        Initialize (instance, binding, remoteAddress);
                }
 
-               void Initialize (InstanceContext instance,
+               internal ClientBase (ChannelFactory<TChannel> factory)
+               {
+                       ChannelFactory = factory;
+               }
+
+               internal virtual void Initialize (InstanceContext instance,
                        string endpointConfigurationName, EndpointAddress remoteAddress)
                {
-                       factory = new ChannelFactory<TChannel> (endpointConfigurationName, remoteAddress);
+                       ChannelFactory = new ChannelFactory<TChannel> (endpointConfigurationName, remoteAddress);
                }
 
-               void Initialize (InstanceContext instance,
+               internal virtual void Initialize (InstanceContext instance,
                        Binding binding, EndpointAddress remoteAddress)
                {
-                       factory = new ChannelFactory<TChannel> (binding, remoteAddress);
+                       ChannelFactory = new ChannelFactory<TChannel> (binding, remoteAddress);
                }
 
                public ChannelFactory<TChannel> ChannelFactory {
                        get { return factory; }
+                       internal set {
+                               factory = value;
+                               factory.OwnerClientBase = this;
+                       }
                }
 
-#if !NET_2_1
                public ClientCredentials ClientCredentials {
                        get { return ChannelFactory.Credentials; }
                }
-#endif
 
                public ServiceEndpoint Endpoint {
                        get { return factory.Endpoint; }
@@ -156,15 +185,11 @@ namespace System.ServiceModel
                public IClientChannel InnerChannel {
                        get {
                                if (inner_channel == null)
-                                       inner_channel = (ClientRuntimeChannel) (object) factory.CreateChannel ();
+                                       inner_channel = (IClientChannel) (object) CreateChannel ();
                                return inner_channel;
                        }
                }
 
-               internal ClientRuntimeChannel InnerRuntimeChannel {
-                       get { return (ClientRuntimeChannel) InnerChannel; }
-               }
-
                protected TChannel Channel {
                        get { return (TChannel) (object) InnerChannel; }
                }
@@ -188,13 +213,35 @@ namespace System.ServiceModel
                        InnerChannel.DisplayInitializationUI ();
                }
 
-#if NET_2_1
                protected T GetDefaultValueForInitialization<T> ()
                {
                        return default (T);
                }
 
-               IAsyncResult delegate_async;
+               //IAsyncResult delegate_async;
+
+               void RunCompletedCallback (SendOrPostCallback callback, InvokeAsyncCompletedEventArgs args)
+               {
+#if !MOONLIGHT
+                       callback (args);
+#else
+                       object dispatcher = dispatcher_main_property.GetValue (null, null);
+                       if (dispatcher == null) {
+                               callback (args);
+                               return;
+                       }
+                       EventHandler a = delegate {
+                               try {
+                                       callback (args); 
+                                       //Console.WriteLine ("ClientBase<TChannel>: operationCompletedCallback is successfully done (unless the callback has further async operations)");
+                               } catch (Exception ex) {
+                                       //Console.WriteLine ("ClientBase<TChannel> caught an error during operationCompletedCallback: " + ex);
+                                       throw;
+                               }
+                       };
+                       dispatcher_begin_invoke_method.Invoke (dispatcher, new object [] {a, new object [] {this, new EventArgs ()}});
+#endif
+               }
 
                protected void InvokeAsync (BeginOperationDelegate beginOperationDelegate,
                        object [] inValues, EndOperationDelegate endOperationDelegate,
@@ -204,28 +251,37 @@ namespace System.ServiceModel
                                throw new ArgumentNullException ("beginOperationDelegate");
                        if (endOperationDelegate == null)
                                throw new ArgumentNullException ("endOperationDelegate");
-                       if (delegate_async != null)
-                               throw new InvalidOperationException ("Another async operation is in progress");
-
-                       var bw = new BackgroundWorker ();
-                       bw.DoWork += delegate (object o, DoWorkEventArgs e) {
-                               delegate_async = beginOperationDelegate (inValues, null, userState);
-                       };
-                       bw.RunWorkerCompleted += delegate (object o, RunWorkerCompletedEventArgs e) {
-                               var ret = endOperationDelegate (delegate_async);
-                               if (operationCompletedCallback != null)
-                                       operationCompletedCallback (ret);
-                               delegate_async = null;
+                       //if (delegate_async != null)
+                       //      throw new InvalidOperationException ("Another async operation is in progress");
+
+                       AsyncCallback cb = delegate (IAsyncResult ar) {
+                               object [] results = null;
+                               Exception error = null;
+                               bool cancelled = false; // FIXME: fill it in case it is cancelled
+                               try {
+                                       results = endOperationDelegate (ar);
+                               } catch (Exception ex) {
+                                       error = ex;
+                               }
+                               try {
+                                       if (operationCompletedCallback != null)
+                                               RunCompletedCallback (operationCompletedCallback, new InvokeAsyncCompletedEventArgs (results, error, cancelled, userState));
+                               } catch (Exception ex) {
+                                       //Console.WriteLine ("Exception during operationCompletedCallback" + ex);
+                                       throw;
+                               }
+                               //Console.WriteLine ("System.ServiceModel.ClientBase<TChannel>: web service invocation is successfully done (operationCompletedCallback may not be).");
                        };
-                       bw.RunWorkerAsync ();
+                       begin_async_result = beginOperationDelegate (inValues, cb, userState);
                }
-#endif
-               
+               IAsyncResult begin_async_result;
+
+#if !MOONLIGHT
                void IDisposable.Dispose ()
                {
                        Close ();
                }
-
+#endif
                protected virtual TChannel CreateChannel ()
                {
                        return ChannelFactory.CreateChannel ();
@@ -238,53 +294,45 @@ namespace System.ServiceModel
 
                #region ICommunicationObject implementation
 
-               [MonoTODO]
                IAsyncResult ICommunicationObject.BeginOpen (
                        AsyncCallback callback, object state)
                {
                        return InnerChannel.BeginOpen (callback, state);
                }
 
-               [MonoTODO]
                IAsyncResult ICommunicationObject.BeginOpen (
                        TimeSpan timeout, AsyncCallback callback, object state)
                {
                        return InnerChannel.BeginOpen (timeout, callback, state);
                }
 
-               [MonoTODO]
                void ICommunicationObject.EndOpen (IAsyncResult result)
                {
                        InnerChannel.EndOpen (result);
                }
 
-               [MonoTODO]
                IAsyncResult ICommunicationObject.BeginClose (
                        AsyncCallback callback, object state)
                {
                        return InnerChannel.BeginClose (callback, state);
                }
 
-               [MonoTODO]
                IAsyncResult ICommunicationObject.BeginClose (
                        TimeSpan timeout, AsyncCallback callback, object state)
                {
                        return InnerChannel.BeginClose (timeout, callback, state);
                }
 
-               [MonoTODO]
                void ICommunicationObject.EndClose (IAsyncResult result)
                {
                        InnerChannel.EndClose (result);
                }
 
-               [MonoTODO]
                void ICommunicationObject.Close (TimeSpan timeout)
                {
                        InnerChannel.Close (timeout);
                }
 
-               [MonoTODO]
                void ICommunicationObject.Open (TimeSpan timeout)
                {
                        InnerChannel.Open (timeout);
@@ -313,7 +361,6 @@ namespace System.ServiceModel
 
                #endregion
 
-#if NET_2_1
                protected class InvokeAsyncCompletedEventArgs : AsyncCompletedEventArgs
                {
                        internal InvokeAsyncCompletedEventArgs (object [] results, Exception error, bool cancelled, object userState)
@@ -325,119 +372,149 @@ namespace System.ServiceModel
                        public object [] Results { get; private set; }
                }
 
-               protected class ChannelBase<T> : IClientChannel, IOutputChannel, IRequestChannel where T : class
+#if NET_2_1
+               protected internal
+#else
+               internal
+#endif
+               class ChannelBase<T> : IClientChannel, IOutputChannel, IRequestChannel where T : class
                {
-                       ClientBase<T> client;
+                       ServiceEndpoint endpoint;
+                       ChannelFactory factory;
+                       ClientRuntimeChannel inner_channel;
 
                        protected ChannelBase (ClientBase<T> client)
+                               : this (client.Endpoint, client.ChannelFactory)
                        {
-                               this.client = client;
                        }
 
-                       [MonoTODO]
+                       internal ChannelBase (ServiceEndpoint endpoint, ChannelFactory factory)
+                       {
+                               this.endpoint = endpoint;
+                               this.factory = factory;
+                       }
+
+                       internal ClientRuntimeChannel Inner {
+                               get {
+                                       if (inner_channel == null)
+                                               inner_channel = new ClientRuntimeChannel (endpoint, factory, endpoint.Address, null);
+                                       return inner_channel;
+                               }
+                       }
+
+#if !MOONLIGHT
+                       protected object Invoke (string methodName, object [] args)
+                       {
+                               var cd = endpoint.Contract;
+                               var od = cd.Operations.Find (methodName);
+                               if (od == null)
+                                       throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
+                               return Inner.Process (od.SyncMethod, methodName, args);
+                       }
+#endif
+
                        protected IAsyncResult BeginInvoke (string methodName, object [] args, AsyncCallback callback, object state)
                        {
-                               var cd = client.Endpoint.Contract;
+                               var cd = endpoint.Contract;
                                var od = cd.Operations.Find (methodName);
                                if (od == null)
                                        throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
-                               return client.InnerRuntimeChannel.BeginProcess (od.BeginMethod, methodName, args, callback, state);
+                               return Inner.BeginProcess (od.BeginMethod, methodName, args, callback, state);
                        }
 
-                       [MonoTODO]
                        protected object EndInvoke (string methodName, object [] args, IAsyncResult result)
                        {
-                               var cd = client.Endpoint.Contract;
+                               var cd = endpoint.Contract;
                                var od = cd.Operations.Find (methodName);
                                if (od == null)
                                        throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
-                               return client.InnerRuntimeChannel.EndProcess (od.EndMethod, methodName, args, result);
+                               return Inner.EndProcess (od.EndMethod, methodName, args, result);
                        }
 
                        #region ICommunicationObject
 
                        IAsyncResult ICommunicationObject.BeginClose (AsyncCallback callback, object state)
                        {
-                               return client.InnerChannel.BeginClose (callback, state);
+                               return Inner.BeginClose (callback, state);
                        }
 
                        IAsyncResult ICommunicationObject.BeginClose (TimeSpan timeout, AsyncCallback callback, object state)
                        {
-                               return client.InnerChannel.BeginClose (timeout, callback, state);
+                               return Inner.BeginClose (timeout, callback, state);
                        }
 
                        void ICommunicationObject.Close ()
                        {
-                               client.InnerChannel.Close ();
+                               Inner.Close ();
                        }
 
                        void ICommunicationObject.Close (TimeSpan timeout)
                        {
-                               client.InnerChannel.Close (timeout);
+                               Inner.Close (timeout);
                        }
 
                        IAsyncResult ICommunicationObject.BeginOpen (AsyncCallback callback, object state)
                        {
-                               return client.InnerChannel.BeginOpen (callback, state);
+                               return Inner.BeginOpen (callback, state);
                        }
 
                        IAsyncResult ICommunicationObject.BeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
                        {
-                               return client.InnerChannel.BeginOpen (timeout, callback, state);
+                               return Inner.BeginOpen (timeout, callback, state);
                        }
 
                        void ICommunicationObject.Open ()
                        {
-                               client.InnerChannel.Open ();
+                               Inner.Open ();
                        }
 
                        void ICommunicationObject.Open (TimeSpan timeout)
                        {
-                               client.InnerChannel.Open (timeout);
+                               Inner.Open (timeout);
                        }
 
                        void ICommunicationObject.Abort ()
                        {
-                               client.InnerChannel.Abort ();
+                               Inner.Abort ();
                        }
 
                        void ICommunicationObject.EndClose (IAsyncResult result)
                        {
-                               client.InnerChannel.EndClose (result);
+                               Inner.EndClose (result);
                        }
 
                        void ICommunicationObject.EndOpen (IAsyncResult result)
                        {
-                               client.InnerChannel.EndOpen (result);
+                               Inner.EndOpen (result);
                        }
 
                        CommunicationState ICommunicationObject.State {
-                               get { return client.InnerChannel.State; }
+                               get { return Inner.State; }
                        }
 
                        event EventHandler ICommunicationObject.Opened {
-                               add { client.InnerChannel.Opened += value; }
-                               remove { client.InnerChannel.Opened -= value; }
+                               add { Inner.Opened += value; }
+                               remove { Inner.Opened -= value; }
                        }
 
                        event EventHandler ICommunicationObject.Opening {
-                               add { client.InnerChannel.Opening += value; }
-                               remove { client.InnerChannel.Opening -= value; }
+                               add { Inner.Opening += value; }
+                               remove { Inner.Opening -= value; }
                        }
 
                        event EventHandler ICommunicationObject.Closed {
-                               add { client.InnerChannel.Closed += value; }
-                               remove { client.InnerChannel.Closed -= value; }
+                               add { Inner.Closed += value; }
+                               remove { Inner.Closed -= value; }
                        }
 
                        event EventHandler ICommunicationObject.Closing {
-                               add { client.InnerChannel.Closing += value; }
-                               remove { client.InnerChannel.Closing -= value; }
+                               add { Inner.Closing += value; }
+                               remove { Inner.Closing -= value; }
                        }
 
                        event EventHandler ICommunicationObject.Faulted {
-                               add { client.InnerChannel.Faulted += value; }
-                               remove { client.InnerChannel.Faulted -= value; }
+                               add { Inner.Faulted += value; }
+                               remove { Inner.Faulted -= value; }
                        }
 
                        #endregion
@@ -445,43 +522,43 @@ namespace System.ServiceModel
                        #region IClientChannel
 
                        public bool AllowInitializationUI {
-                               get { return client.InnerChannel.AllowInitializationUI; }
-                               set { client.InnerChannel.AllowInitializationUI = value; }
+                               get { return Inner.AllowInitializationUI; }
+                               set { Inner.AllowInitializationUI = value; }
                        }
 
                        public bool DidInteractiveInitialization {
-                               get { return client.InnerChannel.DidInteractiveInitialization; }
+                               get { return Inner.DidInteractiveInitialization; }
                        }
 
                        public Uri Via {
-                               get { return client.InnerChannel.Via; }
+                               get { return Inner.Via; }
                        }
 
                        public IAsyncResult BeginDisplayInitializationUI (
                                AsyncCallback callback, object state)
                        {
-                               return client.InnerChannel.BeginDisplayInitializationUI (callback, state);
+                               return Inner.BeginDisplayInitializationUI (callback, state);
                        }
 
                        public void EndDisplayInitializationUI (
                                IAsyncResult result)
                        {
-                               client.InnerChannel.EndDisplayInitializationUI (result);
+                               Inner.EndDisplayInitializationUI (result);
                        }
 
                        public void DisplayInitializationUI ()
                        {
-                               client.InnerChannel.DisplayInitializationUI ();
+                               Inner.DisplayInitializationUI ();
                        }
 
                        public void Dispose ()
                        {
-                               client.InnerChannel.Dispose ();
+                               Inner.Dispose ();
                        }
 
                        public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived {
-                               add { client.InnerChannel.UnknownMessageReceived += value; }
-                               remove { client.InnerChannel.UnknownMessageReceived -= value; }
+                               add { Inner.UnknownMessageReceived += value; }
+                               remove { Inner.UnknownMessageReceived -= value; }
                        }
 
                        #endregion
@@ -490,38 +567,38 @@ namespace System.ServiceModel
 
                        [MonoTODO]
                        public bool AllowOutputBatching {
-                               get { return client.InnerChannel.AllowOutputBatching; }
+                               get { return Inner.AllowOutputBatching; }
 
-                               set { client.InnerChannel.AllowOutputBatching = value; }
+                               set { Inner.AllowOutputBatching = value; }
                        }
 
                        [MonoTODO]
                        public IInputSession InputSession {
-                               get { return client.InnerChannel.InputSession; }
+                               get { return Inner.InputSession; }
                        }
 
                        public EndpointAddress LocalAddress {
-                               get { return client.InnerChannel.LocalAddress; }
+                               get { return Inner.LocalAddress; }
                        }
 
                        [MonoTODO]
                        public TimeSpan OperationTimeout {
-                               get { return client.InnerChannel.OperationTimeout; }
-                               set { client.InnerChannel.OperationTimeout = value; }
+                               get { return Inner.OperationTimeout; }
+                               set { Inner.OperationTimeout = value; }
                        }
 
                        [MonoTODO]
                        public IOutputSession OutputSession {
-                               get { return client.InnerChannel.OutputSession; }
+                               get { return Inner.OutputSession; }
                        }
 
                        public EndpointAddress RemoteAddress {
-                               get { return client.InnerChannel.RemoteAddress; }
+                               get { return Inner.RemoteAddress; }
                        }
 
                        [MonoTODO]
                        public string SessionId {
-                               get { return client.InnerChannel.SessionId; }
+                               get { return Inner.SessionId; }
                        }
 
                        #endregion
@@ -530,31 +607,31 @@ namespace System.ServiceModel
 
                        IAsyncResult IRequestChannel.BeginRequest (Message message, AsyncCallback callback, object state)
                        {
-                               return ((IRequestChannel) this).BeginRequest (message, client.Endpoint.Binding.SendTimeout, callback, state);
+                               return ((IRequestChannel) this).BeginRequest (message, endpoint.Binding.SendTimeout, callback, state);
                        }
 
                        IAsyncResult IRequestChannel.BeginRequest (Message message, TimeSpan timeout, AsyncCallback callback, object state)
                        {
-                               return client.InnerRuntimeChannel.BeginRequest (message, timeout, callback, state);
+                               return Inner.BeginRequest (message, timeout, callback, state);
                        }
 
                        Message IRequestChannel.EndRequest (IAsyncResult result)
                        {
-                               return client.InnerRuntimeChannel.EndRequest (result);
+                               return Inner.EndRequest (result);
                        }
 
                        Message IRequestChannel.Request (Message message)
                        {
-                               return ((IRequestChannel) this).Request (message, client.Endpoint.Binding.SendTimeout);
+                               return ((IRequestChannel) this).Request (message, endpoint.Binding.SendTimeout);
                        }
 
                        Message IRequestChannel.Request (Message message, TimeSpan timeout)
                        {
-                               return client.InnerRuntimeChannel.Request (message, timeout);
+                               return Inner.Request (message, timeout);
                        }
 
                        EndpointAddress IRequestChannel.RemoteAddress {
-                               get { return client.Endpoint.Address; }
+                               get { return endpoint.Address; }
                        }
 
                        Uri IRequestChannel.Via {
@@ -567,40 +644,39 @@ namespace System.ServiceModel
 
                        IAsyncResult IOutputChannel.BeginSend (Message message, AsyncCallback callback, object state)
                        {
-                               return ((IOutputChannel) this).BeginSend (message, client.Endpoint.Binding.SendTimeout, callback, state);
+                               return ((IOutputChannel) this).BeginSend (message, endpoint.Binding.SendTimeout, callback, state);
                        }
 
                        IAsyncResult IOutputChannel.BeginSend (Message message, TimeSpan timeout, AsyncCallback callback, object state)
                        {
-                               return client.InnerRuntimeChannel.BeginSend (message, timeout, callback, state);
+                               return Inner.BeginSend (message, timeout, callback, state);
                        }
 
                        void IOutputChannel.EndSend (IAsyncResult result)
                        {
-                               client.InnerRuntimeChannel.EndSend (result);
+                               Inner.EndSend (result);
                        }
 
                        void IOutputChannel.Send (Message message)
                        {
-                               ((IOutputChannel) this).Send (message, client.Endpoint.Binding.SendTimeout);
+                               ((IOutputChannel) this).Send (message, endpoint.Binding.SendTimeout);
                        }
 
                        void IOutputChannel.Send (Message message, TimeSpan timeout)
                        {
-                               client.InnerRuntimeChannel.Send (message, timeout);
+                               Inner.Send (message, timeout);
                        }
 
                        #endregion
 
                        IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions {
-                               get { return client.InnerChannel.Extensions; }
+                               get { return Inner.Extensions; }
                        }
 
                        TProperty IChannel.GetProperty<TProperty> ()
                        {
-                               return client.InnerChannel.GetProperty<TProperty> ();
+                               return Inner.GetProperty<TProperty> ();
                        }
                }
-#endif
        }
 }