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> :
45 ICommunicationObject where TChannel : class
47 static InstanceContext initialContxt = new InstanceContext (null);
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;
69 protected delegate IAsyncResult BeginOperationDelegate (object[] inValues, AsyncCallback asyncCallback, object state);
70 protected delegate object[] EndOperationDelegate (IAsyncResult result);
72 protected ClientBase ()
73 : this (initialContxt)
77 protected ClientBase (string endpointConfigurationName)
78 : this (initialContxt, endpointConfigurationName)
82 protected ClientBase (Binding binding, EndpointAddress remoteAddress)
83 : this (initialContxt, binding, remoteAddress)
87 protected ClientBase (string endpointConfigurationName, EndpointAddress remoteAddress)
88 : this (initialContxt, endpointConfigurationName, remoteAddress)
92 protected ClientBase (string endpointConfigurationName, string remoteAddress)
93 : this (initialContxt, endpointConfigurationName, remoteAddress)
97 protected ClientBase (InstanceContext instance)
98 : this (instance, "*")
102 protected ClientBase (InstanceContext instance, string endpointConfigurationName)
104 if (instance == null)
105 throw new ArgumentNullException ("instanceContext");
106 if (endpointConfigurationName == null)
107 throw new ArgumentNullException ("endpointConfigurationName");
109 Initialize (instance, endpointConfigurationName, null);
112 protected ClientBase (InstanceContext instance,
113 string endpointConfigurationName, EndpointAddress remoteAddress)
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");
122 Initialize (instance, endpointConfigurationName, remoteAddress);
125 protected ClientBase (InstanceContext instance,
126 string endpointConfigurationName, string remoteAddress)
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");
135 Initialize (instance, endpointConfigurationName, new EndpointAddress (remoteAddress));
138 protected ClientBase (InstanceContext instance,
139 Binding binding, EndpointAddress remoteAddress)
141 if (instance == null)
142 throw new ArgumentNullException ("instanceContext");
144 throw new ArgumentNullException ("binding");
145 if (remoteAddress == null)
146 throw new ArgumentNullException ("remoteAddress");
148 Initialize (instance, binding, remoteAddress);
152 protected ClientBase (ServiceEndpoint endpoint)
153 : this (null, endpoint)
157 protected ClientBase (InstanceContext instance, ServiceEndpoint endpoint)
158 : this (instance, new ChannelFactory<TChannel> (endpoint))
163 internal ClientBase (ChannelFactory<TChannel> factory)
164 : this (null, factory)
168 internal ClientBase (InstanceContext instance, ChannelFactory<TChannel> factory)
170 // FIXME: use instance
171 ChannelFactory = factory;
174 internal virtual void Initialize (InstanceContext instance,
175 string endpointConfigurationName, EndpointAddress remoteAddress)
177 // FIXME: use instance
178 ChannelFactory = new ChannelFactory<TChannel> (endpointConfigurationName, remoteAddress);
181 internal virtual void Initialize (InstanceContext instance,
182 Binding binding, EndpointAddress remoteAddress)
184 // FIXME: use instance
185 ChannelFactory = new ChannelFactory<TChannel> (binding, remoteAddress);
188 public ChannelFactory<TChannel> ChannelFactory {
189 get { return factory; }
192 factory.OwnerClientBase = this;
196 public ClientCredentials ClientCredentials {
197 get { return ChannelFactory.Credentials; }
200 public ServiceEndpoint Endpoint {
201 get { return factory.Endpoint; }
204 public IClientChannel InnerChannel {
206 if (inner_channel == null)
207 inner_channel = (IClientChannel) (object) CreateChannel ();
208 return inner_channel;
212 protected TChannel Channel {
213 get { return (TChannel) (object) InnerChannel; }
216 public CommunicationState State {
217 get { return inner_channel != null ? inner_channel.State : CommunicationState.Created; }
222 InnerChannel.Abort ();
227 InnerChannel.Close ();
230 public void DisplayInitializationUI ()
232 InnerChannel.DisplayInitializationUI ();
235 protected T GetDefaultValueForInitialization<T> ()
240 //IAsyncResult delegate_async;
242 void RunCompletedCallback (SendOrPostCallback callback, InvokeAsyncCompletedEventArgs args)
247 object dispatcher = dispatcher_main_property.GetValue (null, null);
248 if (dispatcher == null) {
252 EventHandler a = delegate {
255 //Console.WriteLine ("ClientBase<TChannel>: operationCompletedCallback is successfully done (unless the callback has further async operations)");
256 } catch (Exception ex) {
257 //Console.WriteLine ("ClientBase<TChannel> caught an error during operationCompletedCallback: " + ex);
261 dispatcher_begin_invoke_method.Invoke (dispatcher, new object [] {a, new object [] {this, new EventArgs ()}});
265 protected void InvokeAsync (BeginOperationDelegate beginOperationDelegate,
266 object [] inValues, EndOperationDelegate endOperationDelegate,
267 SendOrPostCallback operationCompletedCallback, object userState)
269 if (beginOperationDelegate == null)
270 throw new ArgumentNullException ("beginOperationDelegate");
271 if (endOperationDelegate == null)
272 throw new ArgumentNullException ("endOperationDelegate");
273 //if (delegate_async != null)
274 // throw new InvalidOperationException ("Another async operation is in progress");
276 AsyncCallback cb = delegate (IAsyncResult ar) {
277 object [] results = null;
278 Exception error = null;
279 bool cancelled = false; // FIXME: fill it in case it is cancelled
281 results = endOperationDelegate (ar);
282 } catch (Exception ex) {
286 if (operationCompletedCallback != null)
287 RunCompletedCallback (operationCompletedCallback, new InvokeAsyncCompletedEventArgs (results, error, cancelled, userState));
288 } catch (Exception ex) {
289 //Console.WriteLine ("Exception during operationCompletedCallback" + ex);
292 //Console.WriteLine ("System.ServiceModel.ClientBase<TChannel>: web service invocation is successfully done (operationCompletedCallback may not be).");
294 begin_async_result = beginOperationDelegate (inValues, cb, userState);
296 IAsyncResult begin_async_result;
299 void IDisposable.Dispose ()
304 protected virtual TChannel CreateChannel ()
306 return ChannelFactory.CreateChannel ();
311 InnerChannel.Open ();
314 #region ICommunicationObject implementation
316 IAsyncResult ICommunicationObject.BeginOpen (
317 AsyncCallback callback, object state)
319 return InnerChannel.BeginOpen (callback, state);
322 IAsyncResult ICommunicationObject.BeginOpen (
323 TimeSpan timeout, AsyncCallback callback, object state)
325 return InnerChannel.BeginOpen (timeout, callback, state);
328 void ICommunicationObject.EndOpen (IAsyncResult result)
330 InnerChannel.EndOpen (result);
333 IAsyncResult ICommunicationObject.BeginClose (
334 AsyncCallback callback, object state)
336 return InnerChannel.BeginClose (callback, state);
339 IAsyncResult ICommunicationObject.BeginClose (
340 TimeSpan timeout, AsyncCallback callback, object state)
342 return InnerChannel.BeginClose (timeout, callback, state);
345 void ICommunicationObject.EndClose (IAsyncResult result)
347 InnerChannel.EndClose (result);
350 void ICommunicationObject.Close (TimeSpan timeout)
352 InnerChannel.Close (timeout);
355 void ICommunicationObject.Open (TimeSpan timeout)
357 InnerChannel.Open (timeout);
360 event EventHandler ICommunicationObject.Opening {
361 add { InnerChannel.Opening += value; }
362 remove { InnerChannel.Opening -= value; }
364 event EventHandler ICommunicationObject.Opened {
365 add { InnerChannel.Opened += value; }
366 remove { InnerChannel.Opened -= value; }
368 event EventHandler ICommunicationObject.Closing {
369 add { InnerChannel.Closing += value; }
370 remove { InnerChannel.Closing -= value; }
372 event EventHandler ICommunicationObject.Closed {
373 add { InnerChannel.Closed += value; }
374 remove { InnerChannel.Closed -= value; }
376 event EventHandler ICommunicationObject.Faulted {
377 add { InnerChannel.Faulted += value; }
378 remove { InnerChannel.Faulted -= value; }
383 protected class InvokeAsyncCompletedEventArgs : AsyncCompletedEventArgs
385 internal InvokeAsyncCompletedEventArgs (object [] results, Exception error, bool cancelled, object userState)
386 : base (error, cancelled, userState)
391 public object [] Results { get; private set; }
399 class ChannelBase<T> : IClientChannel, IOutputChannel, IRequestChannel where T : class
401 ServiceEndpoint endpoint;
402 ChannelFactory factory;
403 ClientRuntimeChannel inner_channel;
405 protected ChannelBase (ClientBase<T> client)
406 : this (client.Endpoint, client.ChannelFactory)
410 internal ChannelBase (ServiceEndpoint endpoint, ChannelFactory factory)
412 this.endpoint = endpoint;
413 this.factory = factory;
416 internal ClientRuntimeChannel Inner {
418 if (inner_channel == null)
419 inner_channel = new ClientRuntimeChannel (endpoint, factory, endpoint.Address, null);
420 return inner_channel;
425 protected object Invoke (string methodName, object [] args)
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.Process (od.SyncMethod, methodName, args);
435 protected IAsyncResult BeginInvoke (string methodName, object [] args, AsyncCallback callback, object state)
437 var cd = endpoint.Contract;
438 var od = cd.Operations.Find (methodName);
440 throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
441 return Inner.BeginProcess (od.BeginMethod, methodName, args, callback, state);
444 protected object EndInvoke (string methodName, object [] args, IAsyncResult result)
446 var cd = endpoint.Contract;
447 var od = cd.Operations.Find (methodName);
449 throw new ArgumentException (String.Format ("Operation '{0}' not found in the service contract '{1}' in namespace '{2}'", methodName, cd.Name, cd.Namespace));
450 return Inner.EndProcess (od.EndMethod, methodName, args, result);
453 #region ICommunicationObject
455 IAsyncResult ICommunicationObject.BeginClose (AsyncCallback callback, object state)
457 return Inner.BeginClose (callback, state);
460 IAsyncResult ICommunicationObject.BeginClose (TimeSpan timeout, AsyncCallback callback, object state)
462 return Inner.BeginClose (timeout, callback, state);
465 void ICommunicationObject.Close ()
470 void ICommunicationObject.Close (TimeSpan timeout)
472 Inner.Close (timeout);
475 IAsyncResult ICommunicationObject.BeginOpen (AsyncCallback callback, object state)
477 return Inner.BeginOpen (callback, state);
480 IAsyncResult ICommunicationObject.BeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
482 return Inner.BeginOpen (timeout, callback, state);
485 void ICommunicationObject.Open ()
490 void ICommunicationObject.Open (TimeSpan timeout)
492 Inner.Open (timeout);
495 void ICommunicationObject.Abort ()
500 void ICommunicationObject.EndClose (IAsyncResult result)
502 Inner.EndClose (result);
505 void ICommunicationObject.EndOpen (IAsyncResult result)
507 Inner.EndOpen (result);
510 CommunicationState ICommunicationObject.State {
511 get { return Inner.State; }
514 event EventHandler ICommunicationObject.Opened {
515 add { Inner.Opened += value; }
516 remove { Inner.Opened -= value; }
519 event EventHandler ICommunicationObject.Opening {
520 add { Inner.Opening += value; }
521 remove { Inner.Opening -= value; }
524 event EventHandler ICommunicationObject.Closed {
525 add { Inner.Closed += value; }
526 remove { Inner.Closed -= value; }
529 event EventHandler ICommunicationObject.Closing {
530 add { Inner.Closing += value; }
531 remove { Inner.Closing -= value; }
534 event EventHandler ICommunicationObject.Faulted {
535 add { Inner.Faulted += value; }
536 remove { Inner.Faulted -= value; }
541 #region IClientChannel
543 public bool AllowInitializationUI {
544 get { return Inner.AllowInitializationUI; }
545 set { Inner.AllowInitializationUI = value; }
548 public bool DidInteractiveInitialization {
549 get { return Inner.DidInteractiveInitialization; }
553 get { return Inner.Via; }
556 public IAsyncResult BeginDisplayInitializationUI (
557 AsyncCallback callback, object state)
559 return Inner.BeginDisplayInitializationUI (callback, state);
562 public void EndDisplayInitializationUI (
565 Inner.EndDisplayInitializationUI (result);
568 public void DisplayInitializationUI ()
570 Inner.DisplayInitializationUI ();
573 public void Dispose ()
578 public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived {
579 add { Inner.UnknownMessageReceived += value; }
580 remove { Inner.UnknownMessageReceived -= value; }
585 #region IContextChannel
588 public bool AllowOutputBatching {
589 get { return Inner.AllowOutputBatching; }
591 set { Inner.AllowOutputBatching = value; }
595 public IInputSession InputSession {
596 get { return Inner.InputSession; }
599 public EndpointAddress LocalAddress {
600 get { return Inner.LocalAddress; }
604 public TimeSpan OperationTimeout {
605 get { return Inner.OperationTimeout; }
606 set { Inner.OperationTimeout = value; }
610 public IOutputSession OutputSession {
611 get { return Inner.OutputSession; }
614 public EndpointAddress RemoteAddress {
615 get { return Inner.RemoteAddress; }
619 public string SessionId {
620 get { return Inner.SessionId; }
625 #region IRequestChannel
627 IAsyncResult IRequestChannel.BeginRequest (Message message, AsyncCallback callback, object state)
629 return ((IRequestChannel) this).BeginRequest (message, endpoint.Binding.SendTimeout, callback, state);
632 IAsyncResult IRequestChannel.BeginRequest (Message message, TimeSpan timeout, AsyncCallback callback, object state)
634 return Inner.BeginRequest (message, timeout, callback, state);
637 Message IRequestChannel.EndRequest (IAsyncResult result)
639 return Inner.EndRequest (result);
642 Message IRequestChannel.Request (Message message)
644 return ((IRequestChannel) this).Request (message, endpoint.Binding.SendTimeout);
647 Message IRequestChannel.Request (Message message, TimeSpan timeout)
649 return Inner.Request (message, timeout);
652 EndpointAddress IRequestChannel.RemoteAddress {
653 get { return endpoint.Address; }
656 Uri IRequestChannel.Via {
662 #region IOutputChannel
664 IAsyncResult IOutputChannel.BeginSend (Message message, AsyncCallback callback, object state)
666 return ((IOutputChannel) this).BeginSend (message, endpoint.Binding.SendTimeout, callback, state);
669 IAsyncResult IOutputChannel.BeginSend (Message message, TimeSpan timeout, AsyncCallback callback, object state)
671 return Inner.BeginSend (message, timeout, callback, state);
674 void IOutputChannel.EndSend (IAsyncResult result)
676 Inner.EndSend (result);
679 void IOutputChannel.Send (Message message)
681 ((IOutputChannel) this).Send (message, endpoint.Binding.SendTimeout);
684 void IOutputChannel.Send (Message message, TimeSpan timeout)
686 Inner.Send (message, timeout);
691 IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions {
692 get { return Inner.Extensions; }
695 TProperty IChannel.GetProperty<TProperty> ()
697 return Inner.GetProperty<TProperty> ();