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> :
44 ICommunicationObject where TChannel : class
46 static InstanceContext initialContxt = new InstanceContext (null);
48 static readonly PropertyInfo dispatcher_main_property;
49 static readonly MethodInfo dispatcher_begin_invoke_method;
53 Type dispatcher_type = Type.GetType ("System.Windows.Threading.Dispatcher, System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e", true);
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");
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");
65 ChannelFactory<TChannel> factory;
66 IClientChannel inner_channel;
67 CommunicationState state;
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);
151 internal ClientBase (ChannelFactory<TChannel> factory)
153 ChannelFactory = factory;
156 void Initialize (InstanceContext instance,
157 string endpointConfigurationName, EndpointAddress remoteAddress)
159 ChannelFactory = new ChannelFactory<TChannel> (endpointConfigurationName, remoteAddress);
162 void Initialize (InstanceContext instance,
163 Binding binding, EndpointAddress remoteAddress)
165 ChannelFactory = new ChannelFactory<TChannel> (binding, remoteAddress);
168 public ChannelFactory<TChannel> ChannelFactory {
169 get { return factory; }
172 factory.OwnerClientBase = this;
177 public ClientCredentials ClientCredentials {
178 get { return ChannelFactory.Credentials; }
182 public ServiceEndpoint Endpoint {
183 get { return factory.Endpoint; }
186 public IClientChannel InnerChannel {
188 if (inner_channel == null)
189 inner_channel = (IClientChannel) (object) CreateChannel ();
190 return inner_channel;
194 protected TChannel Channel {
195 get { return (TChannel) (object) InnerChannel; }
198 public CommunicationState State {
199 get { return InnerChannel.State; }
204 InnerChannel.Abort ();
209 InnerChannel.Close ();
212 public void DisplayInitializationUI ()
214 InnerChannel.DisplayInitializationUI ();
217 protected T GetDefaultValueForInitialization<T> ()
222 //IAsyncResult delegate_async;
224 void RunCompletedCallback (SendOrPostCallback callback, InvokeAsyncCompletedEventArgs args)
229 object dispatcher = dispatcher_main_property.GetValue (null, null);
230 if (dispatcher == null) {
234 EventHandler a = delegate {
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);
243 dispatcher_begin_invoke_method.Invoke (dispatcher, new object [] {a, new object [] {this, new EventArgs ()}});
247 protected void InvokeAsync (BeginOperationDelegate beginOperationDelegate,
248 object [] inValues, EndOperationDelegate endOperationDelegate,
249 SendOrPostCallback operationCompletedCallback, object userState)
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");
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
263 results = endOperationDelegate (ar);
264 } catch (Exception ex) {
268 if (operationCompletedCallback != null)
269 RunCompletedCallback (operationCompletedCallback, new InvokeAsyncCompletedEventArgs (results, error, cancelled, userState));
270 } catch (Exception ex) {
271 Console.WriteLine ("Exception during operationCompletedCallback" + ex);
274 Console.WriteLine ("System.ServiceModel.ClientBase<TChannel>: web service invocation is successfully done (operationCompletedCallback may not be).");
276 begin_async_result = beginOperationDelegate (inValues, cb, userState);
278 IAsyncResult begin_async_result;
281 void IDisposable.Dispose ()
286 protected virtual TChannel CreateChannel ()
288 return ChannelFactory.CreateChannel ();
293 InnerChannel.Open ();
296 #region ICommunicationObject implementation
298 IAsyncResult ICommunicationObject.BeginOpen (
299 AsyncCallback callback, object state)
301 return InnerChannel.BeginOpen (callback, state);
304 IAsyncResult ICommunicationObject.BeginOpen (
305 TimeSpan timeout, AsyncCallback callback, object state)
307 return InnerChannel.BeginOpen (timeout, callback, state);
310 void ICommunicationObject.EndOpen (IAsyncResult result)
312 InnerChannel.EndOpen (result);
315 IAsyncResult ICommunicationObject.BeginClose (
316 AsyncCallback callback, object state)
318 return InnerChannel.BeginClose (callback, state);
321 IAsyncResult ICommunicationObject.BeginClose (
322 TimeSpan timeout, AsyncCallback callback, object state)
324 return InnerChannel.BeginClose (timeout, callback, state);
327 void ICommunicationObject.EndClose (IAsyncResult result)
329 InnerChannel.EndClose (result);
332 void ICommunicationObject.Close (TimeSpan timeout)
334 InnerChannel.Close (timeout);
337 void ICommunicationObject.Open (TimeSpan timeout)
339 InnerChannel.Open (timeout);
342 event EventHandler ICommunicationObject.Opening {
343 add { InnerChannel.Opening += value; }
344 remove { InnerChannel.Opening -= value; }
346 event EventHandler ICommunicationObject.Opened {
347 add { InnerChannel.Opened += value; }
348 remove { InnerChannel.Opened -= value; }
350 event EventHandler ICommunicationObject.Closing {
351 add { InnerChannel.Closing += value; }
352 remove { InnerChannel.Closing -= value; }
354 event EventHandler ICommunicationObject.Closed {
355 add { InnerChannel.Closed += value; }
356 remove { InnerChannel.Closed -= value; }
358 event EventHandler ICommunicationObject.Faulted {
359 add { InnerChannel.Faulted += value; }
360 remove { InnerChannel.Faulted -= value; }
365 protected 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 ServiceEndpoint endpoint;
384 ChannelFactory factory;
385 ClientRuntimeChannel inner_channel;
387 protected ChannelBase (ClientBase<T> client)
388 : this (client.Endpoint, client.ChannelFactory)
392 internal ChannelBase (ServiceEndpoint endpoint, ChannelFactory factory)
394 this.endpoint = endpoint;
395 this.factory = factory;
398 internal ClientRuntimeChannel Inner {
400 if (inner_channel == null)
401 inner_channel = new ClientRuntimeChannel (endpoint, factory);
402 return inner_channel;
407 public object Invoke (string methodName, object [] args)
409 var cd = endpoint.Contract;
410 var od = cd.Operations.Find (methodName);
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);
417 protected IAsyncResult BeginInvoke (string methodName, object [] args, AsyncCallback callback, object state)
419 var cd = endpoint.Contract;
420 var od = cd.Operations.Find (methodName);
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);
426 protected object EndInvoke (string methodName, object [] args, IAsyncResult result)
428 var cd = endpoint.Contract;
429 var od = cd.Operations.Find (methodName);
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);
435 #region ICommunicationObject
437 IAsyncResult ICommunicationObject.BeginClose (AsyncCallback callback, object state)
439 return Inner.BeginClose (callback, state);
442 IAsyncResult ICommunicationObject.BeginClose (TimeSpan timeout, AsyncCallback callback, object state)
444 return Inner.BeginClose (timeout, callback, state);
447 void ICommunicationObject.Close ()
452 void ICommunicationObject.Close (TimeSpan timeout)
454 Inner.Close (timeout);
457 IAsyncResult ICommunicationObject.BeginOpen (AsyncCallback callback, object state)
459 return Inner.BeginOpen (callback, state);
462 IAsyncResult ICommunicationObject.BeginOpen (TimeSpan timeout, AsyncCallback callback, object state)
464 return Inner.BeginOpen (timeout, callback, state);
467 void ICommunicationObject.Open ()
472 void ICommunicationObject.Open (TimeSpan timeout)
474 Inner.Open (timeout);
477 void ICommunicationObject.Abort ()
482 void ICommunicationObject.EndClose (IAsyncResult result)
484 Inner.EndClose (result);
487 void ICommunicationObject.EndOpen (IAsyncResult result)
489 Inner.EndOpen (result);
492 CommunicationState ICommunicationObject.State {
493 get { return Inner.State; }
496 event EventHandler ICommunicationObject.Opened {
497 add { Inner.Opened += value; }
498 remove { Inner.Opened -= value; }
501 event EventHandler ICommunicationObject.Opening {
502 add { Inner.Opening += value; }
503 remove { Inner.Opening -= value; }
506 event EventHandler ICommunicationObject.Closed {
507 add { Inner.Closed += value; }
508 remove { Inner.Closed -= value; }
511 event EventHandler ICommunicationObject.Closing {
512 add { Inner.Closing += value; }
513 remove { Inner.Closing -= value; }
516 event EventHandler ICommunicationObject.Faulted {
517 add { Inner.Faulted += value; }
518 remove { Inner.Faulted -= value; }
523 #region IClientChannel
525 public bool AllowInitializationUI {
526 get { return Inner.AllowInitializationUI; }
527 set { Inner.AllowInitializationUI = value; }
530 public bool DidInteractiveInitialization {
531 get { return Inner.DidInteractiveInitialization; }
535 get { return Inner.Via; }
538 public IAsyncResult BeginDisplayInitializationUI (
539 AsyncCallback callback, object state)
541 return Inner.BeginDisplayInitializationUI (callback, state);
544 public void EndDisplayInitializationUI (
547 Inner.EndDisplayInitializationUI (result);
550 public void DisplayInitializationUI ()
552 Inner.DisplayInitializationUI ();
555 public void Dispose ()
560 public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived {
561 add { Inner.UnknownMessageReceived += value; }
562 remove { Inner.UnknownMessageReceived -= value; }
567 #region IContextChannel
570 public bool AllowOutputBatching {
571 get { return Inner.AllowOutputBatching; }
573 set { Inner.AllowOutputBatching = value; }
577 public IInputSession InputSession {
578 get { return Inner.InputSession; }
581 public EndpointAddress LocalAddress {
582 get { return Inner.LocalAddress; }
586 public TimeSpan OperationTimeout {
587 get { return Inner.OperationTimeout; }
588 set { Inner.OperationTimeout = value; }
592 public IOutputSession OutputSession {
593 get { return Inner.OutputSession; }
596 public EndpointAddress RemoteAddress {
597 get { return Inner.RemoteAddress; }
601 public string SessionId {
602 get { return Inner.SessionId; }
607 #region IRequestChannel
609 IAsyncResult IRequestChannel.BeginRequest (Message message, AsyncCallback callback, object state)
611 return ((IRequestChannel) this).BeginRequest (message, endpoint.Binding.SendTimeout, callback, state);
614 IAsyncResult IRequestChannel.BeginRequest (Message message, TimeSpan timeout, AsyncCallback callback, object state)
616 return Inner.BeginRequest (message, timeout, callback, state);
619 Message IRequestChannel.EndRequest (IAsyncResult result)
621 return Inner.EndRequest (result);
624 Message IRequestChannel.Request (Message message)
626 return ((IRequestChannel) this).Request (message, endpoint.Binding.SendTimeout);
629 Message IRequestChannel.Request (Message message, TimeSpan timeout)
631 return Inner.Request (message, timeout);
634 EndpointAddress IRequestChannel.RemoteAddress {
635 get { return endpoint.Address; }
638 Uri IRequestChannel.Via {
644 #region IOutputChannel
646 IAsyncResult IOutputChannel.BeginSend (Message message, AsyncCallback callback, object state)
648 return ((IOutputChannel) this).BeginSend (message, endpoint.Binding.SendTimeout, callback, state);
651 IAsyncResult IOutputChannel.BeginSend (Message message, TimeSpan timeout, AsyncCallback callback, object state)
653 return Inner.BeginSend (message, timeout, callback, state);
656 void IOutputChannel.EndSend (IAsyncResult result)
658 Inner.EndSend (result);
661 void IOutputChannel.Send (Message message)
663 ((IOutputChannel) this).Send (message, endpoint.Binding.SendTimeout);
666 void IOutputChannel.Send (Message message, TimeSpan timeout)
668 Inner.Send (message, timeout);
673 IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions {
674 get { return Inner.Extensions; }
677 TProperty IChannel.GetProperty<TProperty> ()
679 return Inner.GetProperty<TProperty> ();