2 // ClientRuntimeChannel.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 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.Reflection;
30 using System.ServiceModel.Channels;
31 using System.ServiceModel.Description;
32 using System.ServiceModel.Dispatcher;
33 using System.ServiceModel.Security;
34 using System.Threading;
37 namespace System.ServiceModel
45 class ClientRuntimeChannel
46 : CommunicationObject, IClientChannel
48 ClientRuntime runtime;
49 ChannelFactory factory;
50 IRequestChannel request_channel;
51 IOutputChannel output_channel; // could also be IDuplexChannel instance.
54 readonly ProcessDelegate _processDelegate;
56 delegate object ProcessDelegate (MethodBase method, string operationName, object [] parameters);
58 readonly RequestDelegate requestDelegate;
60 delegate Message RequestDelegate (Message msg, TimeSpan timeout);
62 readonly SendDelegate sendDelegate;
64 delegate void SendDelegate (Message msg, TimeSpan timeout);
67 public ClientRuntimeChannel (ServiceEndpoint endpoint,
68 ChannelFactory factory)
70 this.runtime = endpoint.CreateRuntime ();
71 this.factory = factory;
72 _processDelegate = new ProcessDelegate (Process);
73 requestDelegate = new RequestDelegate (Request);
74 sendDelegate = new SendDelegate (Send);
77 AllowInitializationUI = true;
78 OperationTimeout = TimeSpan.FromMinutes (1);
80 // determine operation channel to create.
81 if (factory.OpenedChannelFactory is IChannelFactory<IRequestChannel> ||
82 factory.OpenedChannelFactory is IChannelFactory<IRequestSessionChannel>)
83 SetupRequestChannel ();
85 SetupOutputChannel ();
88 public ClientRuntime Runtime {
89 get { return runtime; }
92 #region IClientChannel
94 bool did_interactive_initialization;
96 public bool AllowInitializationUI { get; set; }
98 public bool DidInteractiveInitialization {
99 get { return did_interactive_initialization; }
103 get { return runtime.Via; }
106 class DelegatingWaitHandle : WaitHandle
108 public DelegatingWaitHandle (IAsyncResult [] results)
110 this.results = results;
113 IAsyncResult [] results;
115 protected override void Dispose (bool disposing)
118 foreach (var r in results)
119 r.AsyncWaitHandle.Close ();
122 public override bool WaitOne ()
124 foreach (var r in results)
125 r.AsyncWaitHandle.WaitOne ();
129 public override bool WaitOne (int millisecondsTimeout)
131 return WaitOne (millisecondsTimeout, false);
134 WaitHandle [] ResultWaitHandles {
136 var arr = new WaitHandle [results.Length];
137 for (int i = 0; i < arr.Length; i++)
138 arr [i] = results [i].AsyncWaitHandle;
143 public override bool WaitOne (int millisecondsTimeout, bool exitContext)
145 return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout, exitContext);
148 public override bool WaitOne (TimeSpan timeout, bool exitContext)
150 return WaitHandle.WaitAll (ResultWaitHandles, timeout, exitContext);
154 class DisplayUIAsyncResult : IAsyncResult
156 public DisplayUIAsyncResult (IAsyncResult [] results)
158 this.results = results;
161 IAsyncResult [] results;
163 internal IAsyncResult [] Results {
164 get { return results; }
167 public object AsyncState {
171 WaitHandle wait_handle;
173 public WaitHandle AsyncWaitHandle {
175 if (wait_handle == null)
176 wait_handle = new DelegatingWaitHandle (results);
181 public bool CompletedSynchronously {
183 foreach (var r in results)
184 if (!r.CompletedSynchronously)
189 public bool IsCompleted {
191 foreach (var r in results)
199 public IAsyncResult BeginDisplayInitializationUI (
200 AsyncCallback callback, object state)
202 OnInitializationUI ();
203 IAsyncResult [] arr = new IAsyncResult [runtime.InteractiveChannelInitializers.Count];
205 foreach (var init in runtime.InteractiveChannelInitializers)
206 arr [i++] = init.BeginDisplayInitializationUI (this, callback, state);
207 return new DisplayUIAsyncResult (arr);
210 public void EndDisplayInitializationUI (
213 DisplayUIAsyncResult r = (DisplayUIAsyncResult) result;
215 foreach (var init in runtime.InteractiveChannelInitializers)
216 init.EndDisplayInitializationUI (r.Results [i++]);
218 did_interactive_initialization = true;
221 public void DisplayInitializationUI ()
223 OnInitializationUI ();
224 foreach (var init in runtime.InteractiveChannelInitializers)
225 init.EndDisplayInitializationUI (init.BeginDisplayInitializationUI (this, null, null));
227 did_interactive_initialization = true;
230 void OnInitializationUI ()
232 if (!AllowInitializationUI && runtime.InteractiveChannelInitializers.Count > 0)
233 throw new InvalidOperationException ("AllowInitializationUI is set to false but the client runtime contains one or more InteractiveChannelInitializers.");
236 public void Dispose ()
241 public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived;
245 #region IContextChannel
248 public bool AllowOutputBatching { get; set; }
250 public IInputSession InputSession {
252 ISessionChannel<IInputSession> ch = request_channel as ISessionChannel<IInputSession>;
253 ch = ch ?? output_channel as ISessionChannel<IInputSession>;
256 var dch = output_channel as ISessionChannel<IDuplexSession>;
257 return dch != null ? dch.Session : null;
261 public EndpointAddress LocalAddress {
263 var dc = OperationChannel as IDuplexChannel;
264 return dc != null ? dc.LocalAddress : null;
269 public TimeSpan OperationTimeout { get; set; }
271 public IOutputSession OutputSession {
273 ISessionChannel<IOutputSession> ch = request_channel as ISessionChannel<IOutputSession>;
274 ch = ch ?? output_channel as ISessionChannel<IOutputSession>;
277 var dch = output_channel as ISessionChannel<IDuplexSession>;
278 return dch != null ? dch.Session : null;
282 public EndpointAddress RemoteAddress {
283 get { return request_channel != null ? request_channel.RemoteAddress : output_channel.RemoteAddress; }
286 public string SessionId {
287 get { return OutputSession != null ? OutputSession.Id : InputSession != null ? InputSession.Id : null; }
292 // CommunicationObject
293 protected internal override TimeSpan DefaultOpenTimeout {
294 get { return factory.DefaultOpenTimeout; }
297 protected internal override TimeSpan DefaultCloseTimeout {
298 get { return factory.DefaultCloseTimeout; }
301 protected override void OnAbort ()
306 protected override IAsyncResult OnBeginClose (
307 TimeSpan timeout, AsyncCallback callback, object state)
309 return factory.BeginClose (timeout, callback, state);
312 protected override void OnEndClose (IAsyncResult result)
314 factory.EndClose (result);
317 protected override void OnClose (TimeSpan timeout)
319 factory.Close (timeout);
322 protected override IAsyncResult OnBeginOpen (
323 TimeSpan timeout, AsyncCallback callback, object state)
325 throw new SystemException ("INTERNAL ERROR: this should not be called (or not supported yet)");
328 protected override void OnEndOpen (IAsyncResult result)
332 protected override void OnOpen (TimeSpan timeout)
334 if (runtime.InteractiveChannelInitializers.Count > 0 && !DidInteractiveInitialization)
335 throw new InvalidOperationException ("The client runtime is assigned interactive channel initializers, and in such case DisplayInitializationUI must be called before the channel is opened.");
340 IChannel OperationChannel {
341 get { return (IChannel) request_channel ?? output_channel; }
344 public T GetProperty<T> () where T : class
346 return OperationChannel.GetProperty<T> ();
349 // IExtensibleObject<IContextChannel>
351 public IExtensionCollection<IContextChannel> Extensions {
352 get { throw new NotImplementedException (); }
355 #region Request/Output processing
357 public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)
359 return _processDelegate.BeginInvoke (method, operationName, parameters, callback, asyncState);
362 public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
365 throw new ArgumentNullException ("result");
366 if (parameters == null)
367 throw new ArgumentNullException ("parameters");
368 // FIXME: the method arguments should be verified to be
369 // identical to the arguments in the corresponding begin method.
370 return _processDelegate.EndInvoke (result);
373 public object Process (MethodBase method, string operationName, object [] parameters)
376 return DoProcess (method, operationName, parameters);
377 } catch (Exception ex) {
378 Console.Write ("Exception in async operation: ");
379 Console.WriteLine (ex);
384 object DoProcess (MethodBase method, string operationName, object [] parameters)
386 if (AllowInitializationUI)
387 DisplayInitializationUI ();
388 OperationDescription od = SelectOperation (method, operationName, parameters);
390 return Request (od, parameters);
392 Output (od, parameters);
397 OperationDescription SelectOperation (MethodBase method, string operationName, object [] parameters)
400 if (Runtime.OperationSelector != null)
401 operation = Runtime.OperationSelector.SelectOperation (method, parameters);
403 operation = operationName;
404 OperationDescription od = factory.Endpoint.Contract.Operations.Find (operation);
406 throw new Exception (String.Format ("OperationDescription for operation '{0}' was not found in its internally-generated contract.", operation));
410 BindingParameterCollection CreateBindingParameters ()
412 BindingParameterCollection pl =
413 new BindingParameterCollection ();
415 ContractDescription cd = factory.Endpoint.Contract;
417 pl.Add (ChannelProtectionRequirements.CreateFromContract (cd));
419 foreach (IEndpointBehavior behavior in factory.Endpoint.Behaviors)
420 behavior.AddBindingParameters (factory.Endpoint, pl);
426 // This handles IDuplexChannel, IOutputChannel, and those for session channels.
427 void SetupOutputChannel ()
429 if (output_channel != null)
432 EndpointAddress address = factory.Endpoint.Address;
433 Uri via = Runtime.Via;
435 var method = factory.OpenedChannelFactory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
436 output_channel = (IOutputChannel) method.Invoke (factory.OpenedChannelFactory, new object [] {address, via});
439 // This handles both IRequestChannel and IRequestSessionChannel.
440 void SetupRequestChannel ()
442 if (request_channel != null)
445 EndpointAddress address = factory.Endpoint.Address;
446 Uri via = Runtime.Via;
448 var method = factory.OpenedChannelFactory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
449 request_channel = (IRequestChannel) method.Invoke (factory.OpenedChannelFactory, new object [] {address, via});
452 void Output (OperationDescription od, object [] parameters)
454 if (output_channel.State != CommunicationState.Opened)
455 output_channel.Open ();
457 ClientOperation op = runtime.Operations [od.Name];
458 Send (CreateRequest (op, parameters), OperationTimeout);
461 object Request (OperationDescription od, object [] parameters)
463 if (OperationChannel.State != CommunicationState.Opened)
464 OperationChannel.Open ();
466 ClientOperation op = runtime.Operations [od.Name];
467 object [] inspections = new object [runtime.MessageInspectors.Count];
468 Message req = CreateRequest (op, parameters);
470 for (int i = 0; i < inspections.Length; i++)
471 inspections [i] = runtime.MessageInspectors [i].BeforeSendRequest (ref req, this);
473 Message res = Request (req, OperationTimeout);
475 MessageFault fault = MessageFault.CreateFault (res, runtime.MaxFaultSize);
476 if (fault.HasDetail && fault is MessageFault.SimpleMessageFault) {
477 MessageFault.SimpleMessageFault simpleFault = fault as MessageFault.SimpleMessageFault;
478 object detail = simpleFault.Detail;
479 Type t = detail.GetType ();
480 Type faultType = typeof (FaultException<>).MakeGenericType (t);
481 object [] constructorParams = new object [] { detail, fault.Reason, fault.Code, fault.Actor };
482 FaultException fe = (FaultException) Activator.CreateInstance (faultType, constructorParams);
486 // given a MessageFault, it is hard to figure out the type of the embedded detail
487 throw new FaultException(fault);
491 for (int i = 0; i < inspections.Length; i++)
492 runtime.MessageInspectors [i].AfterReceiveReply (ref res, inspections [i]);
494 if (op.DeserializeReply)
495 return op.GetFormatter ().DeserializeReply (res, parameters);
500 #region Message-based Request() and Send()
501 // They are internal for ClientBase<T>.ChannelBase use.
502 internal Message Request (Message msg, TimeSpan timeout)
504 if (request_channel != null)
505 return request_channel.Request (msg, timeout);
507 DateTime startTime = DateTime.Now;
508 output_channel.Send (msg, timeout);
509 return ((IDuplexChannel) output_channel).Receive (timeout - (DateTime.Now - startTime));
513 internal IAsyncResult BeginRequest (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
515 return requestDelegate.BeginInvoke (msg, timeout, callback, state);
518 internal Message EndRequest (IAsyncResult result)
520 return requestDelegate.EndInvoke (result);
523 internal void Send (Message msg, TimeSpan timeout)
525 output_channel.Send (msg, timeout);
528 internal IAsyncResult BeginSend (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
530 return sendDelegate.BeginInvoke (msg, timeout, callback, state);
533 internal void EndSend (IAsyncResult result)
535 sendDelegate.EndInvoke (result);
539 Message CreateRequest (ClientOperation op, object [] parameters)
541 MessageVersion version = factory.Endpoint.Binding.MessageVersion;
543 version = MessageVersion.Default;
546 if (op.SerializeRequest)
547 msg = op.GetFormatter ().SerializeRequest (
548 version, parameters);
550 msg = (Message) parameters [0];
552 if (OutputSession != null)
553 msg.Headers.MessageId = new UniqueId (OutputSession.Id);
554 msg.Properties.AllowOutputBatching = AllowOutputBatching;