2009-02-27 Atsushi Enomoto <atsushi@ximian.com>
[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                 : IDisposable, ICommunicationObject where TChannel : class
42         {
43                 static InstanceContext initialContxt = new InstanceContext (null);
44 #if NET_2_1
45                 static readonly PropertyInfo dispatcher_main_property;
46                 static readonly MethodInfo dispatcher_begin_invoke_method;
47
48                 static ClientBase ()
49                 {
50                         foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies ()) {
51                                 if (ass.GetName ().Name != "System.Windows")
52                                         continue;
53                                 Type dispatcher_type = ass.GetType ("System.Windows.Threading.Dispatcher", true);
54                                 dispatcher_main_property = dispatcher_type.GetProperty ("Main", BindingFlags.NonPublic | BindingFlags.Static);
55                                 dispatcher_begin_invoke_method = dispatcher_type.GetMethod ("BeginInvoke", new Type [] {typeof (Delegate), typeof (object [])});
56                         }
57                         if (dispatcher_main_property == null)
58                                 throw new SystemException ("Dispatcher.Main not found");
59                         if (dispatcher_begin_invoke_method == null)
60                                 throw new SystemException ("Dispatcher.BeginInvoke not found");
61                 }
62 #endif
63
64                 ChannelFactory<TChannel> factory;
65                 ChannelBase<TChannel> inner_channel;
66                 CommunicationState state;
67
68                 protected delegate IAsyncResult BeginOperationDelegate (object[] inValues, AsyncCallback asyncCallback, object state);
69                 protected delegate object[] EndOperationDelegate (IAsyncResult result);
70
71                 protected ClientBase ()
72                         : this (initialContxt)
73                 {
74                 }
75
76                 protected ClientBase (string endpointConfigurationName)
77                         : this (initialContxt, endpointConfigurationName)
78                 {
79                 }
80
81                 protected ClientBase (Binding binding, EndpointAddress remoteAddress)
82                         : this (initialContxt, binding, remoteAddress)
83                 {
84                 }
85
86                 protected ClientBase (string endpointConfigurationName, EndpointAddress remoteAddress)
87                         : this (initialContxt, endpointConfigurationName, remoteAddress)
88                 {
89                 }
90
91                 protected ClientBase (string endpointConfigurationName, string remoteAddress)
92                         : this (initialContxt, endpointConfigurationName, remoteAddress)
93                 {
94                 }
95
96                 protected ClientBase (InstanceContext instance)
97                         : this (instance, "*")
98                 {
99                 }
100
101                 protected ClientBase (InstanceContext instance, string endpointConfigurationName)
102                 {
103                         if (instance == null)
104                                 throw new ArgumentNullException ("instanceContext");
105                         if (endpointConfigurationName == null)
106                                 throw new ArgumentNullException ("endpointConfigurationName");
107
108                         Initialize (instance, endpointConfigurationName, null);
109                 }
110
111                 protected ClientBase (InstanceContext instance,
112                         string endpointConfigurationName, EndpointAddress remoteAddress)
113                 {
114                         if (instance == null)
115                                 throw new ArgumentNullException ("instanceContext");
116                         if (endpointConfigurationName == null)
117                                 throw new ArgumentNullException ("endpointConfigurationName");
118                         if (remoteAddress == null)
119                                 throw new ArgumentNullException ("remoteAddress");
120
121                         Initialize (instance, endpointConfigurationName, remoteAddress);
122                 }
123
124                 protected ClientBase (InstanceContext instance,
125                         string endpointConfigurationName, string remoteAddress)
126                 {
127                         if (instance == null)
128                                 throw new ArgumentNullException ("instanceContext");
129                         if (remoteAddress == null)
130                                 throw new ArgumentNullException ("endpointAddress");
131                         if (endpointConfigurationName == null)
132                                 throw new ArgumentNullException ("endpointConfigurationName");
133
134                         Initialize (instance, endpointConfigurationName, new EndpointAddress (remoteAddress));
135                 }
136
137                 protected ClientBase (InstanceContext instance,
138                         Binding binding, EndpointAddress remoteAddress)
139                 {
140                         if (instance == null)
141                                 throw new ArgumentNullException ("instanceContext");
142                         if (binding == null)
143                                 throw new ArgumentNullException ("binding");
144                         if (remoteAddress == null)
145                                 throw new ArgumentNullException ("remoteAddress");
146
147                         Initialize (instance, binding, remoteAddress);
148                 }
149
150                 internal ClientBase (ChannelFactory<TChannel> factory)
151                 {
152                         ChannelFactory = factory;
153                 }
154
155                 void Initialize (InstanceContext instance,
156                         string endpointConfigurationName, EndpointAddress remoteAddress)
157                 {
158                         ChannelFactory = new ChannelFactory<TChannel> (endpointConfigurationName, remoteAddress);
159                 }
160
161                 void Initialize (InstanceContext instance,
162                         Binding binding, EndpointAddress remoteAddress)
163                 {
164                         ChannelFactory = new ChannelFactory<TChannel> (binding, remoteAddress);
165                 }
166
167                 public ChannelFactory<TChannel> ChannelFactory {
168                         get { return factory; }
169                         private set {
170                                 factory = value;
171                                 factory.OwnerClientBase = this;
172                         }
173                 }
174
175 #if !NET_2_1
176                 public ClientCredentials ClientCredentials {
177                         get { return ChannelFactory.Credentials; }
178                 }
179 #endif
180
181                 public ServiceEndpoint Endpoint {
182                         get { return factory.Endpoint; }
183                 }
184
185                 public IClientChannel InnerChannel {
186                         get {
187                                 if (inner_channel == null)
188                                         inner_channel = (ChannelBase<TChannel>) (object) CreateChannel ();
189                                 return inner_channel;
190                         }
191                 }
192
193                 protected TChannel Channel {
194                         get { return (TChannel) (object) InnerChannel; }
195                 }
196
197                 public CommunicationState State {
198                         get { return InnerChannel.State; }
199                 }
200
201                 public void Abort ()
202                 {
203                         InnerChannel.Abort ();
204                 }
205
206                 public void Close ()
207                 {
208                         InnerChannel.Close ();
209                 }
210
211                 public void DisplayInitializationUI ()
212                 {
213                         InnerChannel.DisplayInitializationUI ();
214                 }
215
216 #if NET_2_1
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                         object dispatcher = dispatcher_main_property.GetValue (null, null);
227                         if (dispatcher == null) {
228                                 callback (args);
229                                 return;
230                         }
231                         EventHandler a = delegate {
232                                 try {
233                                         callback (args); 
234                                 } catch (Exception ex) {
235                                         Console.WriteLine ("ClientBase<TChannel> caught an error during operationCompletedCallback: " + ex);
236                                         throw;
237                                 }
238                         };
239                         dispatcher_begin_invoke_method.Invoke (dispatcher, new object [] {a, new object [] {this, new EventArgs ()}});
240                 }
241
242                 protected void InvokeAsync (BeginOperationDelegate beginOperationDelegate,
243                         object [] inValues, EndOperationDelegate endOperationDelegate,
244                         SendOrPostCallback operationCompletedCallback, object userState)
245                 {
246                         if (beginOperationDelegate == null)
247                                 throw new ArgumentNullException ("beginOperationDelegate");
248                         if (endOperationDelegate == null)
249                                 throw new ArgumentNullException ("endOperationDelegate");
250                         //if (delegate_async != null)
251                         //      throw new InvalidOperationException ("Another async operation is in progress");
252
253                         AsyncCallback cb = delegate (IAsyncResult ar) {
254                                 object [] results = null;
255                                 Exception error = null;
256                                 bool cancelled = false; // FIXME: fill it in case it is cancelled
257                                 try {
258                                         results = endOperationDelegate (ar);
259                                 } catch (Exception ex) {
260                                         error = ex;
261                                 }
262                                 try {
263                                         if (operationCompletedCallback != null)
264                                                 RunCompletedCallback (operationCompletedCallback, new InvokeAsyncCompletedEventArgs (results, error, cancelled, userState));
265                                 } catch (Exception ex) {
266                                         Console.WriteLine ("Exception during operationCompletedCallback" + ex);
267                                         throw;
268                                 }
269                                 Console.WriteLine ("System.ServiceModel.ClientBase<TChannel>: web service invocation is successfully done (operationCompletedCallback may not be).");
270                         };
271                         begin_async_result = beginOperationDelegate (inValues, cb, userState);
272                 }
273                 IAsyncResult begin_async_result;
274 #endif
275                 
276                 void IDisposable.Dispose ()
277                 {
278                         Close ();
279                 }
280
281                 protected virtual TChannel CreateChannel ()
282                 {
283                         return ChannelFactory.CreateChannel ();
284                 }
285
286                 public void Open ()
287                 {
288                         InnerChannel.Open ();
289                 }
290
291                 #region ICommunicationObject implementation
292
293                 IAsyncResult ICommunicationObject.BeginOpen (
294                         AsyncCallback callback, object state)
295                 {
296                         return InnerChannel.BeginOpen (callback, state);
297                 }
298
299                 IAsyncResult ICommunicationObject.BeginOpen (
300                         TimeSpan timeout, AsyncCallback callback, object state)
301                 {
302                         return InnerChannel.BeginOpen (timeout, callback, state);
303                 }
304
305                 void ICommunicationObject.EndOpen (IAsyncResult result)
306                 {
307                         InnerChannel.EndOpen (result);
308                 }
309
310                 IAsyncResult ICommunicationObject.BeginClose (
311                         AsyncCallback callback, object state)
312                 {
313                         return InnerChannel.BeginClose (callback, state);
314                 }
315
316                 IAsyncResult ICommunicationObject.BeginClose (
317                         TimeSpan timeout, AsyncCallback callback, object state)
318                 {
319                         return InnerChannel.BeginClose (timeout, callback, state);
320                 }
321
322                 void ICommunicationObject.EndClose (IAsyncResult result)
323                 {
324                         InnerChannel.EndClose (result);
325                 }
326
327                 void ICommunicationObject.Close (TimeSpan timeout)
328                 {
329                         InnerChannel.Close (timeout);
330                 }
331
332                 void ICommunicationObject.Open (TimeSpan timeout)
333                 {
334                         InnerChannel.Open (timeout);
335                 }
336
337                 event EventHandler ICommunicationObject.Opening {
338                         add { InnerChannel.Opening += value; }
339                         remove { InnerChannel.Opening -= value; }
340                 }
341                 event EventHandler ICommunicationObject.Opened {
342                         add { InnerChannel.Opened += value; }
343                         remove { InnerChannel.Opened -= value; }
344                 }
345                 event EventHandler ICommunicationObject.Closing {
346                         add { InnerChannel.Closing += value; }
347                         remove { InnerChannel.Closing -= value; }
348                 }
349                 event EventHandler ICommunicationObject.Closed {
350                         add { InnerChannel.Closed += value; }
351                         remove { InnerChannel.Closed -= value; }
352                 }
353                 event EventHandler ICommunicationObject.Faulted {
354                         add { InnerChannel.Faulted += value; }
355                         remove { InnerChannel.Faulted -= value; }
356                 }
357
358                 #endregion
359
360 #if NET_2_1
361                 protected
362 #else
363                 internal
364 #endif
365                 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                         ClientBase<T> client;
384                         ClientRuntimeChannel inner_channel;
385
386                         protected ChannelBase (ClientBase<T> client)
387                         {
388                                 this.client = client;
389                         }
390
391                         internal ClientRuntimeChannel Inner {
392                                 get {
393                                         if (inner_channel == null)
394                                                 inner_channel = new ClientRuntimeChannel (client.Endpoint.CreateRuntime (), client.ChannelFactory);
395                                         return inner_channel;
396                                 }
397                         }
398
399 #if !NET_2_1
400                         public object Invoke (string methodName, object [] args)
401                         {
402                                 var cd = client.Endpoint.Contract;
403                                 var od = cd.Operations.Find (methodName);
404                                 if (od == null)
405                                         throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
406                                 return Inner.Process (od.SyncMethod, methodName, args);
407                         }
408 #endif
409
410                         protected IAsyncResult BeginInvoke (string methodName, object [] args, AsyncCallback callback, object state)
411                         {
412                                 var cd = client.Endpoint.Contract;
413                                 var od = cd.Operations.Find (methodName);
414                                 if (od == null)
415                                         throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
416                                 return Inner.BeginProcess (od.BeginMethod, methodName, args, callback, state);
417                         }
418
419                         protected object EndInvoke (string methodName, object [] args, IAsyncResult result)
420                         {
421                                 var cd = client.Endpoint.Contract;
422                                 var od = cd.Operations.Find (methodName);
423                                 if (od == null)
424                                         throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
425                                 return Inner.EndProcess (od.EndMethod, methodName, args, result);
426                         }
427
428                         #region ICommunicationObject
429
430                         IAsyncResult ICommunicationObject.BeginClose (AsyncCallback callback, object state)
431                         {
432                                 return Inner.BeginClose (callback, state);
433                         }
434
435                         IAsyncResult ICommunicationObject.BeginClose (TimeSpan timeout, AsyncCallback callback, object state)
436                         {
437                                 return Inner.BeginClose (timeout, callback, state);
438                         }
439
440                         void ICommunicationObject.Close ()
441                         {
442                                 Inner.Close ();
443                         }
444
445                         void ICommunicationObject.Close (TimeSpan timeout)
446                         {
447                                 Inner.Close (timeout);
448                         }
449
450                         IAsyncResult ICommunicationObject.BeginOpen (AsyncCallback callback, object state)
451                         {
452                                 return Inner.BeginOpen (callback, state);
453                         }
454
455                         IAsyncResult ICommunicationObject.BeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
456                         {
457                                 return Inner.BeginOpen (timeout, callback, state);
458                         }
459
460                         void ICommunicationObject.Open ()
461                         {
462                                 Inner.Open ();
463                         }
464
465                         void ICommunicationObject.Open (TimeSpan timeout)
466                         {
467                                 Inner.Open (timeout);
468                         }
469
470                         void ICommunicationObject.Abort ()
471                         {
472                                 Inner.Abort ();
473                         }
474
475                         void ICommunicationObject.EndClose (IAsyncResult result)
476                         {
477                                 Inner.EndClose (result);
478                         }
479
480                         void ICommunicationObject.EndOpen (IAsyncResult result)
481                         {
482                                 Inner.EndOpen (result);
483                         }
484
485                         CommunicationState ICommunicationObject.State {
486                                 get { return Inner.State; }
487                         }
488
489                         event EventHandler ICommunicationObject.Opened {
490                                 add { Inner.Opened += value; }
491                                 remove { Inner.Opened -= value; }
492                         }
493
494                         event EventHandler ICommunicationObject.Opening {
495                                 add { Inner.Opening += value; }
496                                 remove { Inner.Opening -= value; }
497                         }
498
499                         event EventHandler ICommunicationObject.Closed {
500                                 add { Inner.Closed += value; }
501                                 remove { Inner.Closed -= value; }
502                         }
503
504                         event EventHandler ICommunicationObject.Closing {
505                                 add { Inner.Closing += value; }
506                                 remove { Inner.Closing -= value; }
507                         }
508
509                         event EventHandler ICommunicationObject.Faulted {
510                                 add { Inner.Faulted += value; }
511                                 remove { Inner.Faulted -= value; }
512                         }
513
514                         #endregion
515
516                         #region IClientChannel
517
518                         public bool AllowInitializationUI {
519                                 get { return Inner.AllowInitializationUI; }
520                                 set { Inner.AllowInitializationUI = value; }
521                         }
522
523                         public bool DidInteractiveInitialization {
524                                 get { return Inner.DidInteractiveInitialization; }
525                         }
526
527                         public Uri Via {
528                                 get { return Inner.Via; }
529                         }
530
531                         public IAsyncResult BeginDisplayInitializationUI (
532                                 AsyncCallback callback, object state)
533                         {
534                                 return Inner.BeginDisplayInitializationUI (callback, state);
535                         }
536
537                         public void EndDisplayInitializationUI (
538                                 IAsyncResult result)
539                         {
540                                 Inner.EndDisplayInitializationUI (result);
541                         }
542
543                         public void DisplayInitializationUI ()
544                         {
545                                 Inner.DisplayInitializationUI ();
546                         }
547
548                         public void Dispose ()
549                         {
550                                 Inner.Dispose ();
551                         }
552
553                         public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived {
554                                 add { Inner.UnknownMessageReceived += value; }
555                                 remove { Inner.UnknownMessageReceived -= value; }
556                         }
557
558                         #endregion
559
560                         #region IContextChannel
561
562                         [MonoTODO]
563                         public bool AllowOutputBatching {
564                                 get { return Inner.AllowOutputBatching; }
565
566                                 set { Inner.AllowOutputBatching = value; }
567                         }
568
569                         [MonoTODO]
570                         public IInputSession InputSession {
571                                 get { return Inner.InputSession; }
572                         }
573
574                         public EndpointAddress LocalAddress {
575                                 get { return Inner.LocalAddress; }
576                         }
577
578                         [MonoTODO]
579                         public TimeSpan OperationTimeout {
580                                 get { return Inner.OperationTimeout; }
581                                 set { Inner.OperationTimeout = value; }
582                         }
583
584                         [MonoTODO]
585                         public IOutputSession OutputSession {
586                                 get { return Inner.OutputSession; }
587                         }
588
589                         public EndpointAddress RemoteAddress {
590                                 get { return Inner.RemoteAddress; }
591                         }
592
593                         [MonoTODO]
594                         public string SessionId {
595                                 get { return Inner.SessionId; }
596                         }
597
598                         #endregion
599
600                         #region IRequestChannel
601
602                         IAsyncResult IRequestChannel.BeginRequest (Message message, AsyncCallback callback, object state)
603                         {
604                                 return ((IRequestChannel) this).BeginRequest (message, client.Endpoint.Binding.SendTimeout, callback, state);
605                         }
606
607                         IAsyncResult IRequestChannel.BeginRequest (Message message, TimeSpan timeout, AsyncCallback callback, object state)
608                         {
609                                 return Inner.BeginRequest (message, timeout, callback, state);
610                         }
611
612                         Message IRequestChannel.EndRequest (IAsyncResult result)
613                         {
614                                 return Inner.EndRequest (result);
615                         }
616
617                         Message IRequestChannel.Request (Message message)
618                         {
619                                 return ((IRequestChannel) this).Request (message, client.Endpoint.Binding.SendTimeout);
620                         }
621
622                         Message IRequestChannel.Request (Message message, TimeSpan timeout)
623                         {
624                                 return Inner.Request (message, timeout);
625                         }
626
627                         EndpointAddress IRequestChannel.RemoteAddress {
628                                 get { return client.Endpoint.Address; }
629                         }
630
631                         Uri IRequestChannel.Via {
632                                 get { return Via; }
633                         }
634
635                         #endregion
636
637                         #region IOutputChannel
638
639                         IAsyncResult IOutputChannel.BeginSend (Message message, AsyncCallback callback, object state)
640                         {
641                                 return ((IOutputChannel) this).BeginSend (message, client.Endpoint.Binding.SendTimeout, callback, state);
642                         }
643
644                         IAsyncResult IOutputChannel.BeginSend (Message message, TimeSpan timeout, AsyncCallback callback, object state)
645                         {
646                                 return Inner.BeginSend (message, timeout, callback, state);
647                         }
648
649                         void IOutputChannel.EndSend (IAsyncResult result)
650                         {
651                                 Inner.EndSend (result);
652                         }
653
654                         void IOutputChannel.Send (Message message)
655                         {
656                                 ((IOutputChannel) this).Send (message, client.Endpoint.Binding.SendTimeout);
657                         }
658
659                         void IOutputChannel.Send (Message message, TimeSpan timeout)
660                         {
661                                 Inner.Send (message, timeout);
662                         }
663
664                         #endregion
665
666                         IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions {
667                                 get { return Inner.Extensions; }
668                         }
669
670                         TProperty IChannel.GetProperty<TProperty> ()
671                         {
672                                 return Inner.GetProperty<TProperty> ();
673                         }
674                 }
675         }
676 }