2 // generic ClientBase.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2005-2006 Novell, Inc. http://www.novell.com
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
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 using System.ServiceModel.MonoInternal;
38 namespace System.ServiceModel
40 [MonoTODO ("It somehow rejects classes, but dunno how we can do that besides our code wise.")]
41 public abstract class ClientBase<TChannel> :
42 #if !NET_2_1 || MONOTOUCH
45 ICommunicationObject where TChannel : class
47 static InstanceContext initialContxt = new InstanceContext (null);
48 #if NET_2_1 && !MONOTOUCH
49 static readonly PropertyInfo dispatcher_main_property;
50 static readonly MethodInfo dispatcher_begin_invoke_method;
54 Type dispatcher_type = Type.GetType ("System.Windows.Threading.Dispatcher, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", true);
56 dispatcher_main_property = dispatcher_type.GetProperty ("Main", BindingFlags.NonPublic | BindingFlags.Static);
57 if (dispatcher_main_property == null)
58 throw new SystemException ("Dispatcher.Main not found");
60 dispatcher_begin_invoke_method = dispatcher_type.GetMethod ("BeginInvoke", new Type [] {typeof (Delegate), typeof (object [])});
61 if (dispatcher_begin_invoke_method == null)
62 throw new SystemException ("Dispatcher.BeginInvoke not found");
66 ChannelFactory<TChannel> factory;
67 IClientChannel inner_channel;
68 CommunicationState state;
70 protected delegate IAsyncResult BeginOperationDelegate (object[] inValues, AsyncCallback asyncCallback, object state);
71 protected delegate object[] EndOperationDelegate (IAsyncResult result);
73 protected ClientBase ()
74 : this (initialContxt)
78 protected ClientBase (string endpointConfigurationName)
79 : this (initialContxt, endpointConfigurationName)
83 protected ClientBase (Binding binding, EndpointAddress remoteAddress)
84 : this (initialContxt, binding, remoteAddress)
88 protected ClientBase (string endpointConfigurationName, EndpointAddress remoteAddress)
89 : this (initialContxt, endpointConfigurationName, remoteAddress)
93 protected ClientBase (string endpointConfigurationName, string remoteAddress)
94 : this (initialContxt, endpointConfigurationName, remoteAddress)
98 protected ClientBase (InstanceContext instance)
99 : this (instance, "*")
103 protected ClientBase (InstanceContext instance, string endpointConfigurationName)
105 if (instance == null)
106 throw new ArgumentNullException ("instanceContext");
107 if (endpointConfigurationName == null)
108 throw new ArgumentNullException ("endpointConfigurationName");
110 Initialize (instance, endpointConfigurationName, null);
113 protected ClientBase (InstanceContext instance,
114 string endpointConfigurationName, EndpointAddress remoteAddress)
116 if (instance == null)
117 throw new ArgumentNullException ("instanceContext");
118 if (endpointConfigurationName == null)
119 throw new ArgumentNullException ("endpointConfigurationName");
120 if (remoteAddress == null)
121 throw new ArgumentNullException ("remoteAddress");
123 Initialize (instance, endpointConfigurationName, remoteAddress);
126 protected ClientBase (InstanceContext instance,
127 string endpointConfigurationName, string remoteAddress)
129 if (instance == null)
130 throw new ArgumentNullException ("instanceContext");
131 if (remoteAddress == null)
132 throw new ArgumentNullException ("endpointAddress");
133 if (endpointConfigurationName == null)
134 throw new ArgumentNullException ("endpointConfigurationName");
136 Initialize (instance, endpointConfigurationName, new EndpointAddress (remoteAddress));
139 protected ClientBase (InstanceContext instance,
140 Binding binding, EndpointAddress remoteAddress)
142 if (instance == null)
143 throw new ArgumentNullException ("instanceContext");
145 throw new ArgumentNullException ("binding");
146 if (remoteAddress == null)
147 throw new ArgumentNullException ("remoteAddress");
149 Initialize (instance, binding, remoteAddress);
152 internal ClientBase (ChannelFactory<TChannel> factory)
154 ChannelFactory = factory;
157 internal virtual void Initialize (InstanceContext instance,
158 string endpointConfigurationName, EndpointAddress remoteAddress)
160 ChannelFactory = new ChannelFactory<TChannel> (endpointConfigurationName, remoteAddress);
163 internal virtual void Initialize (InstanceContext instance,
164 Binding binding, EndpointAddress remoteAddress)
166 ChannelFactory = new ChannelFactory<TChannel> (binding, remoteAddress);
169 public ChannelFactory<TChannel> ChannelFactory {
170 get { return factory; }
173 factory.OwnerClientBase = this;
177 public ClientCredentials ClientCredentials {
178 get { return ChannelFactory.Credentials; }
181 public ServiceEndpoint Endpoint {
182 get { return factory.Endpoint; }
185 public IClientChannel InnerChannel {
187 if (inner_channel == null)
188 inner_channel = (IClientChannel) (object) CreateChannel ();
189 return inner_channel;
193 protected TChannel Channel {
194 get { return (TChannel) (object) InnerChannel; }
197 public CommunicationState State {
198 get { return InnerChannel.State; }
203 InnerChannel.Abort ();
208 InnerChannel.Close ();
211 public void DisplayInitializationUI ()
213 InnerChannel.DisplayInitializationUI ();
216 protected T GetDefaultValueForInitialization<T> ()
221 //IAsyncResult delegate_async;
223 void RunCompletedCallback (SendOrPostCallback callback, InvokeAsyncCompletedEventArgs args)
225 #if !NET_2_1 || MONOTOUCH
228 object dispatcher = dispatcher_main_property.GetValue (null, null);
229 if (dispatcher == null) {
233 EventHandler a = delegate {
236 //Console.WriteLine ("ClientBase<TChannel>: operationCompletedCallback is successfully done (unless the callback has further async operations)");
237 } catch (Exception ex) {
238 //Console.WriteLine ("ClientBase<TChannel> caught an error during operationCompletedCallback: " + ex);
242 dispatcher_begin_invoke_method.Invoke (dispatcher, new object [] {a, new object [] {this, new EventArgs ()}});
246 protected void InvokeAsync (BeginOperationDelegate beginOperationDelegate,
247 object [] inValues, EndOperationDelegate endOperationDelegate,
248 SendOrPostCallback operationCompletedCallback, object userState)
250 if (beginOperationDelegate == null)
251 throw new ArgumentNullException ("beginOperationDelegate");
252 if (endOperationDelegate == null)
253 throw new ArgumentNullException ("endOperationDelegate");
254 //if (delegate_async != null)
255 // throw new InvalidOperationException ("Another async operation is in progress");
257 AsyncCallback cb = delegate (IAsyncResult ar) {
258 object [] results = null;
259 Exception error = null;
260 bool cancelled = false; // FIXME: fill it in case it is cancelled
262 results = endOperationDelegate (ar);
263 } catch (Exception ex) {
267 if (operationCompletedCallback != null)
268 RunCompletedCallback (operationCompletedCallback, new InvokeAsyncCompletedEventArgs (results, error, cancelled, userState));
269 } catch (Exception ex) {
270 //Console.WriteLine ("Exception during operationCompletedCallback" + ex);
273 //Console.WriteLine ("System.ServiceModel.ClientBase<TChannel>: web service invocation is successfully done (operationCompletedCallback may not be).");
275 begin_async_result = beginOperationDelegate (inValues, cb, userState);
277 IAsyncResult begin_async_result;
279 #if !NET_2_1 || MONOTOUCH
280 void IDisposable.Dispose ()
285 protected virtual TChannel CreateChannel ()
287 return ChannelFactory.CreateChannel ();
292 InnerChannel.Open ();
295 #region ICommunicationObject implementation
297 IAsyncResult ICommunicationObject.BeginOpen (
298 AsyncCallback callback, object state)
300 return InnerChannel.BeginOpen (callback, state);
303 IAsyncResult ICommunicationObject.BeginOpen (
304 TimeSpan timeout, AsyncCallback callback, object state)
306 return InnerChannel.BeginOpen (timeout, callback, state);
309 void ICommunicationObject.EndOpen (IAsyncResult result)
311 InnerChannel.EndOpen (result);
314 IAsyncResult ICommunicationObject.BeginClose (
315 AsyncCallback callback, object state)
317 return InnerChannel.BeginClose (callback, state);
320 IAsyncResult ICommunicationObject.BeginClose (
321 TimeSpan timeout, AsyncCallback callback, object state)
323 return InnerChannel.BeginClose (timeout, callback, state);
326 void ICommunicationObject.EndClose (IAsyncResult result)
328 InnerChannel.EndClose (result);
331 void ICommunicationObject.Close (TimeSpan timeout)
333 InnerChannel.Close (timeout);
336 void ICommunicationObject.Open (TimeSpan timeout)
338 InnerChannel.Open (timeout);
341 event EventHandler ICommunicationObject.Opening {
342 add { InnerChannel.Opening += value; }
343 remove { InnerChannel.Opening -= value; }
345 event EventHandler ICommunicationObject.Opened {
346 add { InnerChannel.Opened += value; }
347 remove { InnerChannel.Opened -= value; }
349 event EventHandler ICommunicationObject.Closing {
350 add { InnerChannel.Closing += value; }
351 remove { InnerChannel.Closing -= value; }
353 event EventHandler ICommunicationObject.Closed {
354 add { InnerChannel.Closed += value; }
355 remove { InnerChannel.Closed -= value; }
357 event EventHandler ICommunicationObject.Faulted {
358 add { InnerChannel.Faulted += value; }
359 remove { InnerChannel.Faulted -= value; }
364 protected class InvokeAsyncCompletedEventArgs : AsyncCompletedEventArgs
366 internal InvokeAsyncCompletedEventArgs (object [] results, Exception error, bool cancelled, object userState)
367 : base (error, cancelled, userState)
372 public object [] Results { get; private set; }
380 class ChannelBase<T> : IClientChannel, IOutputChannel, IRequestChannel where T : class
382 ServiceEndpoint endpoint;
383 ChannelFactory factory;
384 ClientRuntimeChannel inner_channel;
386 protected ChannelBase (ClientBase<T> client)
387 : this (client.Endpoint, client.ChannelFactory)
391 internal ChannelBase (ServiceEndpoint endpoint, ChannelFactory factory)
393 this.endpoint = endpoint;
394 this.factory = factory;
397 internal ClientRuntimeChannel Inner {
399 if (inner_channel == null)
400 inner_channel = new ClientRuntimeChannel (endpoint, factory, endpoint.Address, null);
401 return inner_channel;
405 #if !NET_2_1 || MONOTOUCH
406 protected object Invoke (string methodName, object [] args)
408 var cd = endpoint.Contract;
409 var od = cd.Operations.Find (methodName);
411 throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
412 return Inner.Process (od.SyncMethod, methodName, args);
416 protected IAsyncResult BeginInvoke (string methodName, object [] args, AsyncCallback callback, object state)
418 var cd = endpoint.Contract;
419 var od = cd.Operations.Find (methodName);
421 throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
422 return Inner.BeginProcess (od.BeginMethod, methodName, args, callback, state);
425 protected object EndInvoke (string methodName, object [] args, IAsyncResult result)
427 var cd = endpoint.Contract;
428 var od = cd.Operations.Find (methodName);
430 throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
431 return Inner.EndProcess (od.EndMethod, methodName, args, result);
434 #region ICommunicationObject
436 IAsyncResult ICommunicationObject.BeginClose (AsyncCallback callback, object state)
438 return Inner.BeginClose (callback, state);
441 IAsyncResult ICommunicationObject.BeginClose (TimeSpan timeout, AsyncCallback callback, object state)
443 return Inner.BeginClose (timeout, callback, state);
446 void ICommunicationObject.Close ()
451 void ICommunicationObject.Close (TimeSpan timeout)
453 Inner.Close (timeout);
456 IAsyncResult ICommunicationObject.BeginOpen (AsyncCallback callback, object state)
458 return Inner.BeginOpen (callback, state);
461 IAsyncResult ICommunicationObject.BeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
463 return Inner.BeginOpen (timeout, callback, state);
466 void ICommunicationObject.Open ()
471 void ICommunicationObject.Open (TimeSpan timeout)
473 Inner.Open (timeout);
476 void ICommunicationObject.Abort ()
481 void ICommunicationObject.EndClose (IAsyncResult result)
483 Inner.EndClose (result);
486 void ICommunicationObject.EndOpen (IAsyncResult result)
488 Inner.EndOpen (result);
491 CommunicationState ICommunicationObject.State {
492 get { return Inner.State; }
495 event EventHandler ICommunicationObject.Opened {
496 add { Inner.Opened += value; }
497 remove { Inner.Opened -= value; }
500 event EventHandler ICommunicationObject.Opening {
501 add { Inner.Opening += value; }
502 remove { Inner.Opening -= value; }
505 event EventHandler ICommunicationObject.Closed {
506 add { Inner.Closed += value; }
507 remove { Inner.Closed -= value; }
510 event EventHandler ICommunicationObject.Closing {
511 add { Inner.Closing += value; }
512 remove { Inner.Closing -= value; }
515 event EventHandler ICommunicationObject.Faulted {
516 add { Inner.Faulted += value; }
517 remove { Inner.Faulted -= value; }
522 #region IClientChannel
524 public bool AllowInitializationUI {
525 get { return Inner.AllowInitializationUI; }
526 set { Inner.AllowInitializationUI = value; }
529 public bool DidInteractiveInitialization {
530 get { return Inner.DidInteractiveInitialization; }
534 get { return Inner.Via; }
537 public IAsyncResult BeginDisplayInitializationUI (
538 AsyncCallback callback, object state)
540 return Inner.BeginDisplayInitializationUI (callback, state);
543 public void EndDisplayInitializationUI (
546 Inner.EndDisplayInitializationUI (result);
549 public void DisplayInitializationUI ()
551 Inner.DisplayInitializationUI ();
554 public void Dispose ()
559 public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived {
560 add { Inner.UnknownMessageReceived += value; }
561 remove { Inner.UnknownMessageReceived -= value; }
566 #region IContextChannel
569 public bool AllowOutputBatching {
570 get { return Inner.AllowOutputBatching; }
572 set { Inner.AllowOutputBatching = value; }
576 public IInputSession InputSession {
577 get { return Inner.InputSession; }
580 public EndpointAddress LocalAddress {
581 get { return Inner.LocalAddress; }
585 public TimeSpan OperationTimeout {
586 get { return Inner.OperationTimeout; }
587 set { Inner.OperationTimeout = value; }
591 public IOutputSession OutputSession {
592 get { return Inner.OutputSession; }
595 public EndpointAddress RemoteAddress {
596 get { return Inner.RemoteAddress; }
600 public string SessionId {
601 get { return Inner.SessionId; }
606 #region IRequestChannel
608 IAsyncResult IRequestChannel.BeginRequest (Message message, AsyncCallback callback, object state)
610 return ((IRequestChannel) this).BeginRequest (message, endpoint.Binding.SendTimeout, callback, state);
613 IAsyncResult IRequestChannel.BeginRequest (Message message, TimeSpan timeout, AsyncCallback callback, object state)
615 return Inner.BeginRequest (message, timeout, callback, state);
618 Message IRequestChannel.EndRequest (IAsyncResult result)
620 return Inner.EndRequest (result);
623 Message IRequestChannel.Request (Message message)
625 return ((IRequestChannel) this).Request (message, endpoint.Binding.SendTimeout);
628 Message IRequestChannel.Request (Message message, TimeSpan timeout)
630 return Inner.Request (message, timeout);
633 EndpointAddress IRequestChannel.RemoteAddress {
634 get { return endpoint.Address; }
637 Uri IRequestChannel.Via {
643 #region IOutputChannel
645 IAsyncResult IOutputChannel.BeginSend (Message message, AsyncCallback callback, object state)
647 return ((IOutputChannel) this).BeginSend (message, endpoint.Binding.SendTimeout, callback, state);
650 IAsyncResult IOutputChannel.BeginSend (Message message, TimeSpan timeout, AsyncCallback callback, object state)
652 return Inner.BeginSend (message, timeout, callback, state);
655 void IOutputChannel.EndSend (IAsyncResult result)
657 Inner.EndSend (result);
660 void IOutputChannel.Send (Message message)
662 ((IOutputChannel) this).Send (message, endpoint.Binding.SendTimeout);
665 void IOutputChannel.Send (Message message, TimeSpan timeout)
667 Inner.Send (message, timeout);
672 IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions {
673 get { return Inner.Extensions; }
676 TProperty IChannel.GetProperty<TProperty> ()
678 return Inner.GetProperty<TProperty> ();