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;
37 namespace System.ServiceModel
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
43 static InstanceContext initialContxt = new InstanceContext (null);
45 static readonly PropertyInfo dispatcher_main_property;
46 static readonly MethodInfo dispatcher_begin_invoke_method;
50 foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies ()) {
51 if (ass.GetName ().Name != "System.Windows")
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 [])});
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");
64 ChannelFactory<TChannel> factory;
65 ChannelBase<TChannel> inner_channel;
66 CommunicationState state;
68 protected delegate IAsyncResult BeginOperationDelegate (object[] inValues, AsyncCallback asyncCallback, object state);
69 protected delegate object[] EndOperationDelegate (IAsyncResult result);
71 protected ClientBase ()
72 : this (initialContxt)
76 protected ClientBase (string endpointConfigurationName)
77 : this (initialContxt, endpointConfigurationName)
81 protected ClientBase (Binding binding, EndpointAddress remoteAddress)
82 : this (initialContxt, binding, remoteAddress)
86 protected ClientBase (string endpointConfigurationName, EndpointAddress remoteAddress)
87 : this (initialContxt, endpointConfigurationName, remoteAddress)
91 protected ClientBase (string endpointConfigurationName, string remoteAddress)
92 : this (initialContxt, endpointConfigurationName, remoteAddress)
96 protected ClientBase (InstanceContext instance)
97 : this (instance, "*")
101 protected ClientBase (InstanceContext instance, string endpointConfigurationName)
103 if (instance == null)
104 throw new ArgumentNullException ("instanceContext");
105 if (endpointConfigurationName == null)
106 throw new ArgumentNullException ("endpointConfigurationName");
108 Initialize (instance, endpointConfigurationName, null);
111 protected ClientBase (InstanceContext instance,
112 string endpointConfigurationName, EndpointAddress remoteAddress)
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");
121 Initialize (instance, endpointConfigurationName, remoteAddress);
124 protected ClientBase (InstanceContext instance,
125 string endpointConfigurationName, string remoteAddress)
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");
134 Initialize (instance, endpointConfigurationName, new EndpointAddress (remoteAddress));
137 protected ClientBase (InstanceContext instance,
138 Binding binding, EndpointAddress remoteAddress)
140 if (instance == null)
141 throw new ArgumentNullException ("instanceContext");
143 throw new ArgumentNullException ("binding");
144 if (remoteAddress == null)
145 throw new ArgumentNullException ("remoteAddress");
147 Initialize (instance, binding, remoteAddress);
150 internal ClientBase (ChannelFactory<TChannel> factory)
152 ChannelFactory = factory;
155 void Initialize (InstanceContext instance,
156 string endpointConfigurationName, EndpointAddress remoteAddress)
158 ChannelFactory = new ChannelFactory<TChannel> (endpointConfigurationName, remoteAddress);
161 void Initialize (InstanceContext instance,
162 Binding binding, EndpointAddress remoteAddress)
164 ChannelFactory = new ChannelFactory<TChannel> (binding, remoteAddress);
167 public ChannelFactory<TChannel> ChannelFactory {
168 get { return factory; }
171 factory.OwnerClientBase = this;
176 public ClientCredentials ClientCredentials {
177 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 = (ChannelBase<TChannel>) (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 ();
217 protected T GetDefaultValueForInitialization<T> ()
222 //IAsyncResult delegate_async;
224 void RunCompletedCallback (SendOrPostCallback callback, InvokeAsyncCompletedEventArgs args)
226 object dispatcher = dispatcher_main_property.GetValue (null, null);
227 if (dispatcher == null) {
231 EventHandler a = delegate {
234 } catch (Exception ex) {
235 Console.WriteLine ("ClientBase<TChannel> caught an error during operationCompletedCallback: " + ex);
239 dispatcher_begin_invoke_method.Invoke (dispatcher, new object [] {a, new object [] {this, new EventArgs ()}});
242 protected void InvokeAsync (BeginOperationDelegate beginOperationDelegate,
243 object [] inValues, EndOperationDelegate endOperationDelegate,
244 SendOrPostCallback operationCompletedCallback, object userState)
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");
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
258 results = endOperationDelegate (ar);
259 } catch (Exception ex) {
263 if (operationCompletedCallback != null)
264 RunCompletedCallback (operationCompletedCallback, new InvokeAsyncCompletedEventArgs (results, error, cancelled, userState));
265 } catch (Exception ex) {
266 Console.WriteLine ("Exception during operationCompletedCallback" + ex);
269 Console.WriteLine ("System.ServiceModel.ClientBase<TChannel>: web service invocation is successfully done (operationCompletedCallback may not be).");
271 begin_async_result = beginOperationDelegate (inValues, cb, userState);
273 IAsyncResult begin_async_result;
276 void IDisposable.Dispose ()
281 protected virtual TChannel CreateChannel ()
283 return ChannelFactory.CreateChannel ();
288 InnerChannel.Open ();
291 #region ICommunicationObject implementation
293 IAsyncResult ICommunicationObject.BeginOpen (
294 AsyncCallback callback, object state)
296 return InnerChannel.BeginOpen (callback, state);
299 IAsyncResult ICommunicationObject.BeginOpen (
300 TimeSpan timeout, AsyncCallback callback, object state)
302 return InnerChannel.BeginOpen (timeout, callback, state);
305 void ICommunicationObject.EndOpen (IAsyncResult result)
307 InnerChannel.EndOpen (result);
310 IAsyncResult ICommunicationObject.BeginClose (
311 AsyncCallback callback, object state)
313 return InnerChannel.BeginClose (callback, state);
316 IAsyncResult ICommunicationObject.BeginClose (
317 TimeSpan timeout, AsyncCallback callback, object state)
319 return InnerChannel.BeginClose (timeout, callback, state);
322 void ICommunicationObject.EndClose (IAsyncResult result)
324 InnerChannel.EndClose (result);
327 void ICommunicationObject.Close (TimeSpan timeout)
329 InnerChannel.Close (timeout);
332 void ICommunicationObject.Open (TimeSpan timeout)
334 InnerChannel.Open (timeout);
337 event EventHandler ICommunicationObject.Opening {
338 add { InnerChannel.Opening += value; }
339 remove { InnerChannel.Opening -= value; }
341 event EventHandler ICommunicationObject.Opened {
342 add { InnerChannel.Opened += value; }
343 remove { InnerChannel.Opened -= value; }
345 event EventHandler ICommunicationObject.Closing {
346 add { InnerChannel.Closing += value; }
347 remove { InnerChannel.Closing -= value; }
349 event EventHandler ICommunicationObject.Closed {
350 add { InnerChannel.Closed += value; }
351 remove { InnerChannel.Closed -= value; }
353 event EventHandler ICommunicationObject.Faulted {
354 add { InnerChannel.Faulted += value; }
355 remove { InnerChannel.Faulted -= value; }
365 class InvokeAsyncCompletedEventArgs : AsyncCompletedEventArgs
367 internal InvokeAsyncCompletedEventArgs (object [] results, Exception error, bool cancelled, object userState)
368 : base (error, cancelled, userState)
373 public object [] Results { get; private set; }
381 class ChannelBase<T> : IClientChannel, IOutputChannel, IRequestChannel where T : class
383 ClientBase<T> client;
384 ClientRuntimeChannel inner_channel;
386 protected ChannelBase (ClientBase<T> client)
388 this.client = client;
391 internal ClientRuntimeChannel Inner {
393 if (inner_channel == null)
394 inner_channel = new ClientRuntimeChannel (client.Endpoint.CreateRuntime (), client.ChannelFactory);
395 return inner_channel;
400 public object Invoke (string methodName, object [] args)
402 var cd = client.Endpoint.Contract;
403 var od = cd.Operations.Find (methodName);
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);
410 protected IAsyncResult BeginInvoke (string methodName, object [] args, AsyncCallback callback, object state)
412 var cd = client.Endpoint.Contract;
413 var od = cd.Operations.Find (methodName);
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);
419 protected object EndInvoke (string methodName, object [] args, IAsyncResult result)
421 var cd = client.Endpoint.Contract;
422 var od = cd.Operations.Find (methodName);
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);
428 #region ICommunicationObject
430 IAsyncResult ICommunicationObject.BeginClose (AsyncCallback callback, object state)
432 return Inner.BeginClose (callback, state);
435 IAsyncResult ICommunicationObject.BeginClose (TimeSpan timeout, AsyncCallback callback, object state)
437 return Inner.BeginClose (timeout, callback, state);
440 void ICommunicationObject.Close ()
445 void ICommunicationObject.Close (TimeSpan timeout)
447 Inner.Close (timeout);
450 IAsyncResult ICommunicationObject.BeginOpen (AsyncCallback callback, object state)
452 return Inner.BeginOpen (callback, state);
455 IAsyncResult ICommunicationObject.BeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
457 return Inner.BeginOpen (timeout, callback, state);
460 void ICommunicationObject.Open ()
465 void ICommunicationObject.Open (TimeSpan timeout)
467 Inner.Open (timeout);
470 void ICommunicationObject.Abort ()
475 void ICommunicationObject.EndClose (IAsyncResult result)
477 Inner.EndClose (result);
480 void ICommunicationObject.EndOpen (IAsyncResult result)
482 Inner.EndOpen (result);
485 CommunicationState ICommunicationObject.State {
486 get { return Inner.State; }
489 event EventHandler ICommunicationObject.Opened {
490 add { Inner.Opened += value; }
491 remove { Inner.Opened -= value; }
494 event EventHandler ICommunicationObject.Opening {
495 add { Inner.Opening += value; }
496 remove { Inner.Opening -= value; }
499 event EventHandler ICommunicationObject.Closed {
500 add { Inner.Closed += value; }
501 remove { Inner.Closed -= value; }
504 event EventHandler ICommunicationObject.Closing {
505 add { Inner.Closing += value; }
506 remove { Inner.Closing -= value; }
509 event EventHandler ICommunicationObject.Faulted {
510 add { Inner.Faulted += value; }
511 remove { Inner.Faulted -= value; }
516 #region IClientChannel
518 public bool AllowInitializationUI {
519 get { return Inner.AllowInitializationUI; }
520 set { Inner.AllowInitializationUI = value; }
523 public bool DidInteractiveInitialization {
524 get { return Inner.DidInteractiveInitialization; }
528 get { return Inner.Via; }
531 public IAsyncResult BeginDisplayInitializationUI (
532 AsyncCallback callback, object state)
534 return Inner.BeginDisplayInitializationUI (callback, state);
537 public void EndDisplayInitializationUI (
540 Inner.EndDisplayInitializationUI (result);
543 public void DisplayInitializationUI ()
545 Inner.DisplayInitializationUI ();
548 public void Dispose ()
553 public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived {
554 add { Inner.UnknownMessageReceived += value; }
555 remove { Inner.UnknownMessageReceived -= value; }
560 #region IContextChannel
563 public bool AllowOutputBatching {
564 get { return Inner.AllowOutputBatching; }
566 set { Inner.AllowOutputBatching = value; }
570 public IInputSession InputSession {
571 get { return Inner.InputSession; }
574 public EndpointAddress LocalAddress {
575 get { return Inner.LocalAddress; }
579 public TimeSpan OperationTimeout {
580 get { return Inner.OperationTimeout; }
581 set { Inner.OperationTimeout = value; }
585 public IOutputSession OutputSession {
586 get { return Inner.OutputSession; }
589 public EndpointAddress RemoteAddress {
590 get { return Inner.RemoteAddress; }
594 public string SessionId {
595 get { return Inner.SessionId; }
600 #region IRequestChannel
602 IAsyncResult IRequestChannel.BeginRequest (Message message, AsyncCallback callback, object state)
604 return ((IRequestChannel) this).BeginRequest (message, client.Endpoint.Binding.SendTimeout, callback, state);
607 IAsyncResult IRequestChannel.BeginRequest (Message message, TimeSpan timeout, AsyncCallback callback, object state)
609 return Inner.BeginRequest (message, timeout, callback, state);
612 Message IRequestChannel.EndRequest (IAsyncResult result)
614 return Inner.EndRequest (result);
617 Message IRequestChannel.Request (Message message)
619 return ((IRequestChannel) this).Request (message, client.Endpoint.Binding.SendTimeout);
622 Message IRequestChannel.Request (Message message, TimeSpan timeout)
624 return Inner.Request (message, timeout);
627 EndpointAddress IRequestChannel.RemoteAddress {
628 get { return client.Endpoint.Address; }
631 Uri IRequestChannel.Via {
637 #region IOutputChannel
639 IAsyncResult IOutputChannel.BeginSend (Message message, AsyncCallback callback, object state)
641 return ((IOutputChannel) this).BeginSend (message, client.Endpoint.Binding.SendTimeout, callback, state);
644 IAsyncResult IOutputChannel.BeginSend (Message message, TimeSpan timeout, AsyncCallback callback, object state)
646 return Inner.BeginSend (message, timeout, callback, state);
649 void IOutputChannel.EndSend (IAsyncResult result)
651 Inner.EndSend (result);
654 void IOutputChannel.Send (Message message)
656 ((IOutputChannel) this).Send (message, client.Endpoint.Binding.SendTimeout);
659 void IOutputChannel.Send (Message message, TimeSpan timeout)
661 Inner.Send (message, timeout);
666 IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions {
667 get { return Inner.Extensions; }
670 TProperty IChannel.GetProperty<TProperty> ()
672 return Inner.GetProperty<TProperty> ();