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;
36 namespace System.ServiceModel
44 class ClientRuntimeChannel
45 : CommunicationObject, IClientChannel
47 ClientRuntime runtime;
48 ChannelFactory factory;
49 IRequestChannel request_channel;
50 IOutputChannel output_channel; // could also be IDuplexChannel instance.
53 readonly ProcessDelegate _processDelegate;
55 delegate object ProcessDelegate (MethodBase method, string operationName, object [] parameters);
57 readonly RequestDelegate requestDelegate;
59 delegate Message RequestDelegate (Message msg, TimeSpan timeout);
61 readonly SendDelegate sendDelegate;
63 delegate void SendDelegate (Message msg, TimeSpan timeout);
66 public ClientRuntimeChannel (ServiceEndpoint endpoint,
67 ChannelFactory factory)
69 this.runtime = endpoint.CreateRuntime ();
70 this.factory = factory;
71 _processDelegate = new ProcessDelegate (Process);
72 requestDelegate = new RequestDelegate (Request);
73 sendDelegate = new SendDelegate (Send);
76 AllowInitializationUI = true;
77 OperationTimeout = TimeSpan.FromMinutes (1);
79 // determine operation channel to create.
80 if (factory.OpenedChannelFactory is IChannelFactory<IRequestChannel> ||
81 factory.OpenedChannelFactory is IChannelFactory<IRequestSessionChannel>)
82 SetupRequestChannel ();
84 SetupOutputChannel ();
87 public ClientRuntime Runtime {
88 get { return runtime; }
91 #region IClientChannel
93 bool did_interactive_initialization;
95 public bool AllowInitializationUI { get; set; }
97 public bool DidInteractiveInitialization {
98 get { return did_interactive_initialization; }
102 get { return runtime.Via; }
105 class DelegatingWaitHandle : WaitHandle
107 public DelegatingWaitHandle (IAsyncResult [] results)
109 this.results = results;
112 IAsyncResult [] results;
114 protected override void Dispose (bool disposing)
117 foreach (var r in results)
118 r.AsyncWaitHandle.Close ();
121 public override bool WaitOne ()
123 foreach (var r in results)
124 r.AsyncWaitHandle.WaitOne ();
128 public override bool WaitOne (int millisecondsTimeout)
130 return WaitOne (millisecondsTimeout, false);
133 WaitHandle [] ResultWaitHandles {
135 var arr = new WaitHandle [results.Length];
136 for (int i = 0; i < arr.Length; i++)
137 arr [i] = results [i].AsyncWaitHandle;
142 public override bool WaitOne (int millisecondsTimeout, bool exitContext)
144 return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout, exitContext);
147 public override bool WaitOne (TimeSpan timeout, bool exitContext)
149 return WaitHandle.WaitAll (ResultWaitHandles, timeout, exitContext);
153 class DisplayUIAsyncResult : IAsyncResult
155 public DisplayUIAsyncResult (IAsyncResult [] results)
157 this.results = results;
160 IAsyncResult [] results;
162 internal IAsyncResult [] Results {
163 get { return results; }
166 public object AsyncState {
170 WaitHandle wait_handle;
172 public WaitHandle AsyncWaitHandle {
174 if (wait_handle == null)
175 wait_handle = new DelegatingWaitHandle (results);
180 public bool CompletedSynchronously {
182 foreach (var r in results)
183 if (!r.CompletedSynchronously)
188 public bool IsCompleted {
190 foreach (var r in results)
198 public IAsyncResult BeginDisplayInitializationUI (
199 AsyncCallback callback, object state)
201 OnInitializationUI ();
202 IAsyncResult [] arr = new IAsyncResult [runtime.InteractiveChannelInitializers.Count];
204 foreach (var init in runtime.InteractiveChannelInitializers)
205 arr [i++] = init.BeginDisplayInitializationUI (this, callback, state);
206 return new DisplayUIAsyncResult (arr);
209 public void EndDisplayInitializationUI (
212 DisplayUIAsyncResult r = (DisplayUIAsyncResult) result;
214 foreach (var init in runtime.InteractiveChannelInitializers)
215 init.EndDisplayInitializationUI (r.Results [i++]);
217 did_interactive_initialization = true;
220 public void DisplayInitializationUI ()
222 OnInitializationUI ();
223 foreach (var init in runtime.InteractiveChannelInitializers)
224 init.EndDisplayInitializationUI (init.BeginDisplayInitializationUI (this, null, null));
226 did_interactive_initialization = true;
229 void OnInitializationUI ()
231 if (!AllowInitializationUI && runtime.InteractiveChannelInitializers.Count > 0)
232 throw new InvalidOperationException ("AllowInitializationUI is set to false but the client runtime contains one or more InteractiveChannelInitializers.");
235 public void Dispose ()
240 public event EventHandler<UnknownMessageReceivedEventArgs> UnknownMessageReceived;
244 #region IContextChannel
247 public bool AllowOutputBatching { get; set; }
249 public IInputSession InputSession {
251 ISessionChannel<IInputSession> ch = request_channel as ISessionChannel<IInputSession>;
252 ch = ch ?? output_channel as ISessionChannel<IInputSession>;
253 return ch != null ? ch.Session : null;
257 public EndpointAddress LocalAddress {
259 var dc = OperationChannel as IDuplexChannel;
260 return dc != null ? dc.LocalAddress : null;
265 public TimeSpan OperationTimeout { get; set; }
267 public IOutputSession OutputSession {
269 ISessionChannel<IOutputSession> ch = request_channel as ISessionChannel<IOutputSession>;
270 ch = ch ?? output_channel as ISessionChannel<IOutputSession>;
271 return ch != null ? ch.Session : null;
275 public EndpointAddress RemoteAddress {
276 get { return request_channel != null ? request_channel.RemoteAddress : output_channel.RemoteAddress; }
279 public string SessionId {
280 get { return OutputSession != null ? OutputSession.Id : InputSession != null ? InputSession.Id : null; }
285 // CommunicationObject
286 protected internal override TimeSpan DefaultOpenTimeout {
287 get { return factory.DefaultOpenTimeout; }
290 protected internal override TimeSpan DefaultCloseTimeout {
291 get { return factory.DefaultCloseTimeout; }
294 protected override void OnAbort ()
299 protected override IAsyncResult OnBeginClose (
300 TimeSpan timeout, AsyncCallback callback, object state)
302 return factory.BeginClose (timeout, callback, state);
305 protected override void OnEndClose (IAsyncResult result)
307 factory.EndClose (result);
310 protected override void OnClose (TimeSpan timeout)
312 factory.Close (timeout);
315 protected override IAsyncResult OnBeginOpen (
316 TimeSpan timeout, AsyncCallback callback, object state)
318 throw new SystemException ("INTERNAL ERROR: this should not be called (or not supported yet)");
321 protected override void OnEndOpen (IAsyncResult result)
325 protected override void OnOpen (TimeSpan timeout)
327 if (runtime.InteractiveChannelInitializers.Count > 0 && !DidInteractiveInitialization)
328 throw new InvalidOperationException ("The client runtime is assigned interactive channel initializers, and in such case DisplayInitializationUI must be called before the channel is opened.");
333 IChannel OperationChannel {
334 get { return (IChannel) request_channel ?? output_channel; }
337 public T GetProperty<T> () where T : class
339 return OperationChannel.GetProperty<T> ();
342 // IExtensibleObject<IContextChannel>
344 public IExtensionCollection<IContextChannel> Extensions {
345 get { throw new NotImplementedException (); }
348 #region Request/Output processing
350 public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)
352 return _processDelegate.BeginInvoke (method, operationName, parameters, callback, asyncState);
355 public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
358 throw new ArgumentNullException ("result");
359 if (parameters == null)
360 throw new ArgumentNullException ("parameters");
361 // FIXME: the method arguments should be verified to be
362 // identical to the arguments in the corresponding begin method.
363 return _processDelegate.EndInvoke (result);
366 public object Process (MethodBase method, string operationName, object [] parameters)
369 return DoProcess (method, operationName, parameters);
370 } catch (Exception ex) {
371 Console.Write ("Exception in async operation: ");
372 Console.WriteLine (ex);
377 object DoProcess (MethodBase method, string operationName, object [] parameters)
379 if (AllowInitializationUI)
380 DisplayInitializationUI ();
381 OperationDescription od = SelectOperation (method, operationName, parameters);
383 return Request (od, parameters);
385 Output (od, parameters);
390 OperationDescription SelectOperation (MethodBase method, string operationName, object [] parameters)
393 if (Runtime.OperationSelector != null)
394 operation = Runtime.OperationSelector.SelectOperation (method, parameters);
396 operation = operationName;
397 OperationDescription od = factory.Endpoint.Contract.Operations.Find (operation);
399 throw new Exception (String.Format ("OperationDescription for operation '{0}' was not found in its internally-generated contract.", operation));
403 BindingParameterCollection CreateBindingParameters ()
405 BindingParameterCollection pl =
406 new BindingParameterCollection ();
408 ContractDescription cd = factory.Endpoint.Contract;
410 pl.Add (ChannelProtectionRequirements.CreateFromContract (cd));
412 foreach (IEndpointBehavior behavior in factory.Endpoint.Behaviors)
413 behavior.AddBindingParameters (factory.Endpoint, pl);
419 // This handles IDuplexChannel, IOutputChannel, and those for session channels.
420 void SetupOutputChannel ()
422 if (output_channel != null)
425 EndpointAddress address = factory.Endpoint.Address;
426 Uri via = Runtime.Via;
428 var method = factory.OpenedChannelFactory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
429 output_channel = (IOutputChannel) method.Invoke (factory.OpenedChannelFactory, new object [] {address, via});
432 // This handles both IRequestChannel and IRequestSessionChannel.
433 void SetupRequestChannel ()
435 if (request_channel != null)
438 EndpointAddress address = factory.Endpoint.Address;
439 Uri via = Runtime.Via;
441 var method = factory.OpenedChannelFactory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
442 request_channel = (IRequestChannel) method.Invoke (factory.OpenedChannelFactory, new object [] {address, via});
445 void Output (OperationDescription od, object [] parameters)
447 if (output_channel.State != CommunicationState.Opened)
448 output_channel.Open ();
450 ClientOperation op = runtime.Operations [od.Name];
451 Send (CreateRequest (op, parameters), OperationTimeout);
454 object Request (OperationDescription od, object [] parameters)
456 if (OperationChannel.State != CommunicationState.Opened)
457 OperationChannel.Open ();
459 ClientOperation op = runtime.Operations [od.Name];
460 object [] inspections = new object [runtime.MessageInspectors.Count];
461 Message req = CreateRequest (op, parameters);
463 for (int i = 0; i < inspections.Length; i++)
464 inspections [i] = runtime.MessageInspectors [i].BeforeSendRequest (ref req, this);
466 Message res = Request (req, OperationTimeout);
468 MessageFault fault = MessageFault.CreateFault (res, runtime.MaxFaultSize);
469 if (fault.HasDetail && fault is MessageFault.SimpleMessageFault) {
470 MessageFault.SimpleMessageFault simpleFault = fault as MessageFault.SimpleMessageFault;
471 object detail = simpleFault.Detail;
472 Type t = detail.GetType ();
473 Type faultType = typeof (FaultException<>).MakeGenericType (t);
474 object [] constructorParams = new object [] { detail, fault.Reason, fault.Code, fault.Actor };
475 FaultException fe = (FaultException) Activator.CreateInstance (faultType, constructorParams);
479 // given a MessageFault, it is hard to figure out the type of the embedded detail
480 throw new FaultException(fault);
484 for (int i = 0; i < inspections.Length; i++)
485 runtime.MessageInspectors [i].AfterReceiveReply (ref res, inspections [i]);
487 if (op.DeserializeReply)
488 return op.GetFormatter ().DeserializeReply (res, parameters);
493 #region Message-based Request() and Send()
494 // They are internal for ClientBase<T>.ChannelBase use.
495 internal Message Request (Message msg, TimeSpan timeout)
497 if (request_channel != null)
498 return request_channel.Request (msg, timeout);
500 DateTime startTime = DateTime.Now;
501 output_channel.Send (msg, timeout);
502 return ((IDuplexChannel) output_channel).Receive (timeout - (DateTime.Now - startTime));
506 internal IAsyncResult BeginRequest (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
508 return requestDelegate.BeginInvoke (msg, timeout, callback, state);
511 internal Message EndRequest (IAsyncResult result)
513 return requestDelegate.EndInvoke (result);
516 internal void Send (Message msg, TimeSpan timeout)
518 output_channel.Send (msg, timeout);
521 internal IAsyncResult BeginSend (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
523 return sendDelegate.BeginInvoke (msg, timeout, callback, state);
526 internal void EndSend (IAsyncResult result)
528 sendDelegate.EndInvoke (result);
532 Message CreateRequest (ClientOperation op, object [] parameters)
534 MessageVersion version = factory.Endpoint.Binding.MessageVersion;
536 version = MessageVersion.Default;
539 if (op.SerializeRequest)
540 msg = op.GetFormatter ().SerializeRequest (
541 version, parameters);
543 msg = (Message) parameters [0];
544 msg.Properties.AllowOutputBatching = AllowOutputBatching;