2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ClientBase.cs
1 //
2 // generic ClientBase.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005-2006 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.ComponentModel;
31 using System.Reflection;
32 using System.ServiceModel.Channels;
33 using System.ServiceModel.Description;
34 using System.ServiceModel.Dispatcher;
35 using System.Threading;
36
37 namespace System.ServiceModel
38 {
39         [MonoTODO ("It somehow rejects classes, but dunno how we can do that besides our code wise.")]
40         public abstract class ClientBase<TChannel> :
41 #if !NET_2_1
42                 IDisposable,
43 #endif
44                 ICommunicationObject where TChannel : class
45         {
46                 static InstanceContext initialContxt = new InstanceContext (null);
47 #if NET_2_1
48                 static readonly PropertyInfo dispatcher_main_property;
49                 static readonly MethodInfo dispatcher_begin_invoke_method;
50
51                 static ClientBase ()
52                 {
53                         Type dispatcher_type = Type.GetType ("System.Windows.Threading.Dispatcher, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", true);
54
55                         dispatcher_main_property = dispatcher_type.GetProperty ("Main", BindingFlags.NonPublic | BindingFlags.Static);
56                         if (dispatcher_main_property == null)
57                                 throw new SystemException ("Dispatcher.Main not found");
58
59                         dispatcher_begin_invoke_method = dispatcher_type.GetMethod ("BeginInvoke", new Type [] {typeof (Delegate), typeof (object [])});
60                         if (dispatcher_begin_invoke_method == null)
61                                 throw new SystemException ("Dispatcher.BeginInvoke not found");
62                 }
63 #endif
64
65                 ChannelFactory<TChannel> factory;
66                 IClientChannel inner_channel;
67                 CommunicationState state;
68
69                 protected delegate IAsyncResult BeginOperationDelegate (object[] inValues, AsyncCallback asyncCallback, object state);
70                 protected delegate object[] EndOperationDelegate (IAsyncResult result);
71
72                 protected ClientBase ()
73                         : this (initialContxt)
74                 {
75                 }
76
77                 protected ClientBase (string endpointConfigurationName)
78                         : this (initialContxt, endpointConfigurationName)
79                 {
80                 }
81
82                 protected ClientBase (Binding binding, EndpointAddress remoteAddress)
83                         : this (initialContxt, binding, remoteAddress)
84                 {
85                 }
86
87                 protected ClientBase (string endpointConfigurationName, EndpointAddress remoteAddress)
88                         : this (initialContxt, endpointConfigurationName, remoteAddress)
89                 {
90                 }
91
92                 protected ClientBase (string endpointConfigurationName, string remoteAddress)
93                         : this (initialContxt, endpointConfigurationName, remoteAddress)
94                 {
95                 }
96
97                 protected ClientBase (InstanceContext instance)
98                         : this (instance, "*")
99                 {
100                 }
101
102                 protected ClientBase (InstanceContext instance, string endpointConfigurationName)
103                 {
104                         if (instance == null)
105                                 throw new ArgumentNullException ("instanceContext");
106                         if (endpointConfigurationName == null)
107                                 throw new ArgumentNullException ("endpointConfigurationName");
108
109                         Initialize (instance, endpointConfigurationName, null);
110                 }
111
112                 protected ClientBase (InstanceContext instance,
113                         string endpointConfigurationName, EndpointAddress remoteAddress)
114                 {
115                         if (instance == null)
116                                 throw new ArgumentNullException ("instanceContext");
117                         if (endpointConfigurationName == null)
118                                 throw new ArgumentNullException ("endpointConfigurationName");
119                         if (remoteAddress == null)
120                                 throw new ArgumentNullException ("remoteAddress");
121
122                         Initialize (instance, endpointConfigurationName, remoteAddress);
123                 }
124
125                 protected ClientBase (InstanceContext instance,
126                         string endpointConfigurationName, string remoteAddress)
127                 {
128                         if (instance == null)
129                                 throw new ArgumentNullException ("instanceContext");
130                         if (remoteAddress == null)
131                                 throw new ArgumentNullException ("endpointAddress");
132                         if (endpointConfigurationName == null)
133                                 throw new ArgumentNullException ("endpointConfigurationName");
134
135                         Initialize (instance, endpointConfigurationName, new EndpointAddress (remoteAddress));
136                 }
137
138                 protected ClientBase (InstanceContext instance,
139                         Binding binding, EndpointAddress remoteAddress)
140                 {
141                         if (instance == null)
142                                 throw new ArgumentNullException ("instanceContext");
143                         if (binding == null)
144                                 throw new ArgumentNullException ("binding");
145                         if (remoteAddress == null)
146                                 throw new ArgumentNullException ("remoteAddress");
147
148                         Initialize (instance, binding, remoteAddress);
149                 }
150
151                 internal ClientBase (ChannelFactory<TChannel> factory)
152                 {
153                         ChannelFactory = factory;
154                 }
155
156                 void Initialize (InstanceContext instance,
157                         string endpointConfigurationName, EndpointAddress remoteAddress)
158                 {
159                         ChannelFactory = new ChannelFactory<TChannel> (endpointConfigurationName, remoteAddress);
160                 }
161
162                 void Initialize (InstanceContext instance,
163                         Binding binding, EndpointAddress remoteAddress)
164                 {
165                         ChannelFactory = new ChannelFactory<TChannel> (binding, remoteAddress);
166                 }
167
168                 public ChannelFactory<TChannel> ChannelFactory {
169                         get { return factory; }
170                         private set {
171                                 factory = value;
172                                 factory.OwnerClientBase = this;
173                         }
174                 }
175
176 #if !NET_2_1
177                 public ClientCredentials ClientCredentials {
178                         get { return ChannelFactory.Credentials; }
179                 }
180 #endif
181
182                 public ServiceEndpoint Endpoint {
183                         get { return factory.Endpoint; }
184                 }
185
186                 public IClientChannel InnerChannel {
187                         get {
188                                 if (inner_channel == null)
189                                         inner_channel = (IClientChannel) (object) CreateChannel ();
190                                 return inner_channel;
191                         }
192                 }
193
194                 protected TChannel Channel {
195                         get { return (TChannel) (object) InnerChannel; }
196                 }
197
198                 public CommunicationState State {
199                         get { return InnerChannel.State; }
200                 }
201
202                 public void Abort ()
203                 {
204                         InnerChannel.Abort ();
205                 }
206
207                 public void Close ()
208                 {
209                         InnerChannel.Close ();
210                 }
211
212                 public void DisplayInitializationUI ()
213                 {
214                         InnerChannel.DisplayInitializationUI ();
215                 }
216
217                 protected T GetDefaultValueForInitialization<T> ()
218                 {
219                         return default (T);
220                 }
221
222                 //IAsyncResult delegate_async;
223
224                 void RunCompletedCallback (SendOrPostCallback callback, InvokeAsyncCompletedEventArgs args)
225                 {
226 #if !NET_2_1
227                         callback (args);
228 #else
229                         object dispatcher = dispatcher_main_property.GetValue (null, null);
230                         if (dispatcher == null) {
231                                 callback (args);
232                                 return;
233                         }
234                         EventHandler a = delegate {
235                                 try {
236                                         callback (args); 
237                                         Console.WriteLine ("ClientBase<TChannel>: operationCompletedCallback is successfully done (unless the callback has further async operations)");
238                                 } catch (Exception ex) {
239                                         Console.WriteLine ("ClientBase<TChannel> caught an error during operationCompletedCallback: " + ex);
240                                         throw;
241                                 }
242                         };
243                         dispatcher_begin_invoke_method.Invoke (dispatcher, new object [] {a, new object [] {this, new EventArgs ()}});
244 #endif
245                 }
246
247                 protected void InvokeAsync (BeginOperationDelegate beginOperationDelegate,
248                         object [] inValues, EndOperationDelegate endOperationDelegate,
249                         SendOrPostCallback operationCompletedCallback, object userState)
250                 {
251                         if (beginOperationDelegate == null)
252                                 throw new ArgumentNullException ("beginOperationDelegate");
253                         if (endOperationDelegate == null)
254                                 throw new ArgumentNullException ("endOperationDelegate");
255                         //if (delegate_async != null)
256                         //      throw new InvalidOperationException ("Another async operation is in progress");
257
258                         AsyncCallback cb = delegate (IAsyncResult ar) {
259                                 object [] results = null;
260                                 Exception error = null;
261                                 bool cancelled = false; // FIXME: fill it in case it is cancelled
262                                 try {
263                                         results = endOperationDelegate (ar);
264                                 } catch (Exception ex) {
265                                         error = ex;
266                                 }
267                                 try {
268                                         if (operationCompletedCallback != null)
269                                                 RunCompletedCallback (operationCompletedCallback, new InvokeAsyncCompletedEventArgs (results, error, cancelled, userState));
270                                 } catch (Exception ex) {
271                                         Console.WriteLine ("Exception during operationCompletedCallback" + ex);
272                                         throw;
273                                 }
274                                 Console.WriteLine ("System.ServiceModel.ClientBase<TChannel>: web service invocation is successfully done (operationCompletedCallback may not be).");
275                         };
276                         begin_async_result = beginOperationDelegate (inValues, cb, userState);
277                 }
278                 IAsyncResult begin_async_result;
279
280 #if !NET_2_1
281                 void IDisposable.Dispose ()
282                 {
283                         Close ();
284                 }
285 #endif
286                 protected virtual TChannel CreateChannel ()
287                 {
288                         return ChannelFactory.CreateChannel ();
289                 }
290
291                 public void Open ()
292                 {
293                         InnerChannel.Open ();
294                 }
295
296                 #region ICommunicationObject implementation
297
298                 IAsyncResult ICommunicationObject.BeginOpen (
299                         AsyncCallback callback, object state)
300                 {
301                         return InnerChannel.BeginOpen (callback, state);
302                 }
303
304                 IAsyncResult ICommunicationObject.BeginOpen (
305                         TimeSpan timeout, AsyncCallback callback, object state)
306                 {
307                         return InnerChannel.BeginOpen (timeout, callback, state);
308                 }
309
310                 void ICommunicationObject.EndOpen (IAsyncResult result)
311                 {
312                         InnerChannel.EndOpen (result);
313                 }
314
315                 IAsyncResult ICommunicationObject.BeginClose (
316                         AsyncCallback callback, object state)
317                 {
318                         return InnerChannel.BeginClose (callback, state);
319                 }
320
321                 IAsyncResult ICommunicationObject.BeginClose (
322                         TimeSpan timeout, AsyncCallback callback, object state)
323                 {
324                         return InnerChannel.BeginClose (timeout, callback, state);
325                 }
326
327                 void ICommunicationObject.EndClose (IAsyncResult result)
328                 {
329                         InnerChannel.EndClose (result);
330                 }
331
332                 void ICommunicationObject.Close (TimeSpan timeout)
333                 {
334                         InnerChannel.Close (timeout);
335                 }
336
337                 void ICommunicationObject.Open (TimeSpan timeout)
338                 {
339                         InnerChannel.Open (timeout);
340                 }
341
342                 event EventHandler ICommunicationObject.Opening {
343                         add { InnerChannel.Opening += value; }
344                         remove { InnerChannel.Opening -= value; }
345                 }
346                 event EventHandler ICommunicationObject.Opened {
347                         add { InnerChannel.Opened += value; }
348                         remove { InnerChannel.Opened -= value; }
349                 }
350                 event EventHandler ICommunicationObject.Closing {
351                         add { InnerChannel.Closing += value; }
352                         remove { InnerChannel.Closing -= value; }
353                 }
354                 event EventHandler ICommunicationObject.Closed {
355                         add { InnerChannel.Closed += value; }
356                         remove { InnerChannel.Closed -= value; }
357                 }
358                 event EventHandler ICommunicationObject.Faulted {
359                         add { InnerChannel.Faulted += value; }
360                         remove { InnerChannel.Faulted -= value; }
361                 }
362
363                 #endregion
364
365                 protected class InvokeAsyncCompletedEventArgs : AsyncCompletedEventArgs
366                 {
367                         internal InvokeAsyncCompletedEventArgs (object [] results, Exception error, bool cancelled, object userState)
368                                 : base (error, cancelled, userState)
369                         {
370                                 Results = results;
371                         }
372
373                         public object [] Results { get; private set; }
374                 }
375
376 #if NET_2_1
377                 protected internal
378 #else
379                 internal
380 #endif
381                 class ChannelBase<T> : IClientChannel, IOutputChannel, IRequestChannel where T : class
382                 {
383                         ServiceEndpoint endpoint;
384                         ChannelFactory factory;
385                         ClientRuntimeChannel inner_channel;
386
387                         protected ChannelBase (ClientBase<T> client)
388                                 : this (client.Endpoint, client.ChannelFactory)
389                         {
390                         }
391
392                         internal ChannelBase (ServiceEndpoint endpoint, ChannelFactory factory)
393                         {
394                                 this.endpoint = endpoint;
395                                 this.factory = factory;
396                         }
397
398                         internal ClientRuntimeChannel Inner {
399                                 get {
400                                         if (inner_channel == null)
401                                                 inner_channel = new ClientRuntimeChannel (endpoint, factory);
402                                         return inner_channel;
403                                 }
404                         }
405
406 #if !NET_2_1
407                         public object Invoke (string methodName, object [] args)
408                         {
409                                 var cd = endpoint.Contract;
410                                 var od = cd.Operations.Find (methodName);
411                                 if (od == null)
412                                         throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
413                                 return Inner.Process (od.SyncMethod, methodName, args);
414                         }
415 #endif
416
417                         protected IAsyncResult BeginInvoke (string methodName, object [] args, AsyncCallback callback, object state)
418                         {
419                                 var cd = endpoint.Contract;
420                                 var od = cd.Operations.Find (methodName);
421                                 if (od == null)
422                                         throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
423                                 return Inner.BeginProcess (od.BeginMethod, methodName, args, callback, state);
424                         }
425
426                         protected object EndInvoke (string methodName, object [] args, IAsyncResult result)
427                         {
428                                 var cd = endpoint.Contract;
429                                 var od = cd.Operations.Find (methodName);
430                                 if (od == null)
431                                         throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
432                                 return Inner.EndProcess (od.EndMethod, methodName, args, result);
433                         }
434
435                         #region ICommunicationObject
436
437                         IAsyncResult ICommunicationObject.BeginClose (AsyncCallback callback, object state)
438                         {
439                                 return Inner.BeginClose (callback, state);
440                         }
441
442                         IAsyncResult ICommunicationObject.BeginClose (TimeSpan timeout, AsyncCallback callback, object state)
443                         {
444                                 return Inner.BeginClose (timeout, callback, state);
445                         }
446
447                         void ICommunicationObject.Close ()
448                         {
449                                 Inner.Close ();
450                         }
451
452                         void ICommunicationObject.Close (TimeSpan timeout)
453                         {
454                                 Inner.Close (timeout);
455                         }
456
457                         IAsyncResult ICommunicationObject.BeginOpen (AsyncCallback callback, object state)
458                         {
459                                 return Inner.BeginOpen (callback, state);
460                         }
461
462                         IAsyncResult ICommunicationObject.BeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
463                         {
464                                 return Inner.BeginOpen (timeout, callback, state);
465                         }
466
467                         void ICommunicationObject.Open ()
468                         {
469                                 Inner.Open ();
470                         }
471
472                         void ICommunicationObject.Open (TimeSpan timeout)
473                         {
474                                 Inner.Open (timeout);
475                         }
476
477                         void ICommunicationObject.Abort ()
478                         {
479                                 Inner.Abort ();
480                         }
481
482                         void ICommunicationObject.EndClose (IAsyncResult result)
483                         {
484                                 Inner.EndClose (result);
485                         }
486
487                         void ICommunicationObject.EndOpen (IAsyncResult result)
488                         {
489                                 Inner.EndOpen (result);
490                         }
491
492                         CommunicationState ICommunicationObject.State {
493                                 get { return Inner.State; }
494                         }
495
496                         event EventHandler ICommunicationObject.Opened {
497                                 add { Inner.Opened += value; }
498                                 remove { Inner.Opened -= value; }
499                         }
500
501                         event EventHandler ICommunicationObject.Opening {
502                                 add { Inner.Opening += value; }
503                                 remove { Inner.Opening -= value; }
504                         }
505
506                         event EventHandler ICommunicationObject.Closed {
507                                 add { Inner.Closed += value; }
508                                 remove { Inner.Closed -= value; }
509                         }
510
511                         event EventHandler ICommunicationObject.Closing {
512                                 add { Inner.Closing += value; }
513                                 remove { Inner.Closing -= value; }
514                         }
515
516                         event EventHandler ICommunicationObject.Faulted {
517                                 add { Inner.Faulted += value; }
518                                 remove { Inner.Faulted -= value; }
519                         }
520
521                         #endregion
522
523                         #region IClientChannel
524
525                         public bool AllowInitializationUI {
526                                 get { return Inner.AllowInitializationUI; }
527                                 set { Inner.AllowInitializationUI = value; }
528                         }
529
530                         public bool DidInteractiveInitialization {
531                                 get { return Inner.DidInteractiveInitialization; }
532                         }
533
534                         public Uri Via {
535                                 get { return Inner.Via; }
536                         }
537
538                         public IAsyncResult BeginDisplayInitializationUI (
539                                 AsyncCallback callback, object state)
540                         {
541                                 return Inner.BeginDisplayInitializationUI (callback, state);
542                         }
543
544                         public void EndDisplayInitializationUI (
545                                 IAsyncResult result)
546                         {
547                                 Inner.EndDisplayInitializationUI (result);
548                         }
549
550                         public void DisplayInitializationUI ()
551                         {
552                                 Inner.DisplayInitializationUI ();
553                         }
554
555                         public void Dispose ()
556                         {
557                                 Inner.Dispose ();
558                         }
559
560                         public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived {
561                                 add { Inner.UnknownMessageReceived += value; }
562                                 remove { Inner.UnknownMessageReceived -= value; }
563                         }
564
565                         #endregion
566
567                         #region IContextChannel
568
569                         [MonoTODO]
570                         public bool AllowOutputBatching {
571                                 get { return Inner.AllowOutputBatching; }
572
573                                 set { Inner.AllowOutputBatching = value; }
574                         }
575
576                         [MonoTODO]
577                         public IInputSession InputSession {
578                                 get { return Inner.InputSession; }
579                         }
580
581                         public EndpointAddress LocalAddress {
582                                 get { return Inner.LocalAddress; }
583                         }
584
585                         [MonoTODO]
586                         public TimeSpan OperationTimeout {
587                                 get { return Inner.OperationTimeout; }
588                                 set { Inner.OperationTimeout = value; }
589                         }
590
591                         [MonoTODO]
592                         public IOutputSession OutputSession {
593                                 get { return Inner.OutputSession; }
594                         }
595
596                         public EndpointAddress RemoteAddress {
597                                 get { return Inner.RemoteAddress; }
598                         }
599
600                         [MonoTODO]
601                         public string SessionId {
602                                 get { return Inner.SessionId; }
603                         }
604
605                         #endregion
606
607                         #region IRequestChannel
608
609                         IAsyncResult IRequestChannel.BeginRequest (Message message, AsyncCallback callback, object state)
610                         {
611                                 return ((IRequestChannel) this).BeginRequest (message, endpoint.Binding.SendTimeout, callback, state);
612                         }
613
614                         IAsyncResult IRequestChannel.BeginRequest (Message message, TimeSpan timeout, AsyncCallback callback, object state)
615                         {
616                                 return Inner.BeginRequest (message, timeout, callback, state);
617                         }
618
619                         Message IRequestChannel.EndRequest (IAsyncResult result)
620                         {
621                                 return Inner.EndRequest (result);
622                         }
623
624                         Message IRequestChannel.Request (Message message)
625                         {
626                                 return ((IRequestChannel) this).Request (message, endpoint.Binding.SendTimeout);
627                         }
628
629                         Message IRequestChannel.Request (Message message, TimeSpan timeout)
630                         {
631                                 return Inner.Request (message, timeout);
632                         }
633
634                         EndpointAddress IRequestChannel.RemoteAddress {
635                                 get { return endpoint.Address; }
636                         }
637
638                         Uri IRequestChannel.Via {
639                                 get { return Via; }
640                         }
641
642                         #endregion
643
644                         #region IOutputChannel
645
646                         IAsyncResult IOutputChannel.BeginSend (Message message, AsyncCallback callback, object state)
647                         {
648                                 return ((IOutputChannel) this).BeginSend (message, endpoint.Binding.SendTimeout, callback, state);
649                         }
650
651                         IAsyncResult IOutputChannel.BeginSend (Message message, TimeSpan timeout, AsyncCallback callback, object state)
652                         {
653                                 return Inner.BeginSend (message, timeout, callback, state);
654                         }
655
656                         void IOutputChannel.EndSend (IAsyncResult result)
657                         {
658                                 Inner.EndSend (result);
659                         }
660
661                         void IOutputChannel.Send (Message message)
662                         {
663                                 ((IOutputChannel) this).Send (message, endpoint.Binding.SendTimeout);
664                         }
665
666                         void IOutputChannel.Send (Message message, TimeSpan timeout)
667                         {
668                                 Inner.Send (message, timeout);
669                         }
670
671                         #endregion
672
673                         IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions {
674                                 get { return Inner.Extensions; }
675                         }
676
677                         TProperty IChannel.GetProperty<TProperty> ()
678                         {
679                                 return Inner.GetProperty<TProperty> ();
680                         }
681                 }
682         }
683 }