2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel / ServiceHostBase.cs
1 //
2 // ServiceHostBase.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005-2006 Novell, Inc.  http://www.novell.com
8 //
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:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
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.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.Collections.ObjectModel;
31 using System.ServiceModel.Channels;
32 using System.ServiceModel.Configuration;
33 using System.ServiceModel.Description;
34 using System.ServiceModel.Dispatcher;
35 using System.ServiceModel.Security;
36 using System.Reflection;
37
38 namespace System.ServiceModel
39 {
40         public abstract class ServiceHostBase
41                 : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable
42         {
43                 ServiceCredentials credentials;
44                 ServiceDescription description;
45                 UriSchemeKeyedCollection base_addresses;
46                 TimeSpan open_timeout, close_timeout, instance_idle_timeout;
47                 ServiceThrottle throttle;
48                 List<InstanceContext> contexts;
49                 ReadOnlyCollection<InstanceContext> exposed_contexts;
50                 ChannelDispatcherCollection channel_dispatchers;
51                 IDictionary<string,ContractDescription> contracts;
52                 int flow_limit = int.MaxValue;
53                 IExtensionCollection<ServiceHostBase> extensions;
54
55                 protected ServiceHostBase ()
56                 {
57                         open_timeout = DefaultOpenTimeout;
58                         close_timeout = DefaultCloseTimeout;
59
60                         credentials = new ServiceCredentials ();
61                         throttle = new ServiceThrottle ();
62                         contexts = new List<InstanceContext> ();
63                         exposed_contexts = new ReadOnlyCollection<InstanceContext> (contexts);
64                         channel_dispatchers = new ChannelDispatcherCollection (this);
65                 }
66
67                 public event EventHandler<UnknownMessageReceivedEventArgs>
68                         UnknownMessageReceived;
69
70                 public ReadOnlyCollection<Uri> BaseAddresses {
71                         get { return new ReadOnlyCollection<Uri> (base_addresses.InternalItems); }
72                 }
73
74                 internal Uri CreateUri (string sheme, Uri relatieUri) {
75                         Uri baseUri = base_addresses.Contains (sheme) ? base_addresses [sheme] : null;
76
77                         if (relatieUri == null)
78                                 return baseUri;
79                         if (relatieUri.IsAbsoluteUri)
80                                 return relatieUri;
81                         if (baseUri == null)
82                                 return null;
83                         return new Uri (baseUri, relatieUri);
84                 }
85
86                 public ChannelDispatcherCollection ChannelDispatchers {
87                         get { return channel_dispatchers; }
88                 }
89
90                 public ServiceAuthorizationBehavior Authorization {
91                         get;
92                         private set;
93                 }
94
95                 [MonoTODO]
96                 public ServiceCredentials Credentials {
97                         get { return credentials; }
98                 }
99
100                 public ServiceDescription Description {
101                         get { return description; }
102                 }
103
104                 protected IDictionary<string,ContractDescription> ImplementedContracts {
105                         get { return contracts; }
106                 }
107
108                 [MonoTODO]
109                 public IExtensionCollection<ServiceHostBase> Extensions {
110                         get {
111                                 if (extensions == null)
112                                         extensions = new ExtensionCollection<ServiceHostBase> (this);
113                                 return extensions;
114                         }
115                 }
116
117                 protected internal override TimeSpan DefaultCloseTimeout {
118                         get { return DefaultCommunicationTimeouts.Instance.CloseTimeout; }
119                 }
120
121                 protected internal override TimeSpan DefaultOpenTimeout {
122                         get { return DefaultCommunicationTimeouts.Instance.OpenTimeout; }
123                 }
124
125                 public TimeSpan CloseTimeout {
126                         get { return close_timeout; }
127                         set { close_timeout = value; }
128                 }
129
130                 public TimeSpan OpenTimeout {
131                         get { return open_timeout; }
132                         set { open_timeout = value; }
133                 }
134
135                 public int ManualFlowControlLimit {
136                         get { return flow_limit; }
137                         set { flow_limit = value; }
138                 }
139
140                 public ServiceEndpoint AddServiceEndpoint (
141                         string implementedContract, Binding binding, string address)
142                 {
143                         return AddServiceEndpoint (implementedContract,
144                                 binding,
145                                 new Uri (address, UriKind.RelativeOrAbsolute));
146                 }
147
148                 public ServiceEndpoint AddServiceEndpoint (
149                         string implementedContract, Binding binding,
150                         string address, Uri listenUri)
151                 {
152                         Uri uri = new Uri (address, UriKind.RelativeOrAbsolute);
153                         return AddServiceEndpoint (
154                                 implementedContract, binding, uri, uri);
155                 }
156
157                 public ServiceEndpoint AddServiceEndpoint (
158                         string implementedContract, Binding binding,
159                         Uri address)
160                 {
161                         return AddServiceEndpoint (implementedContract, binding, address, address);
162                 }
163
164                 public ServiceEndpoint AddServiceEndpoint (
165                         string implementedContract, Binding binding,
166                         Uri address, Uri listenUri)
167                 {
168                         EndpointAddress ea = BuildEndpointAddress (address, binding);
169                         ContractDescription cd = GetContract (implementedContract);
170                         if (cd == null)
171                                 throw new InvalidOperationException (String.Format ("Contract '{0}' was not found in the implemented contracts in this service host.", implementedContract));
172                         return AddServiceEndpointCore (cd, binding, ea, listenUri);
173                 }
174
175                 Type PopulateType (string typeName)
176                 {
177                         Type type = Type.GetType (typeName);
178                         if (type != null)
179                                 return type;
180                         foreach (ContractDescription cd in ImplementedContracts.Values) {
181                                 type = cd.ContractType.Assembly.GetType (typeName);
182                                 if (type != null)
183                                         return type;
184                         }
185                         return null;
186                 }
187
188                 ContractDescription GetContract (string typeName)
189                 {
190                         //FIXME: hack hack hack
191                         ImplementedContracts ["IHttpGetHelpPageAndMetadataContract"] =
192                                 ContractDescription.GetContract (typeof (IHttpGetHelpPageAndMetadataContract));
193
194                         // FIXME: As long as I tried, *only* IMetadataExchange
195                         // is the exception case that does not require full
196                         // type name. Hence I treat it as a special case.
197                         if (typeName == ServiceMetadataBehavior.MexContractName) {
198                                 if (!Description.Behaviors.Contains (typeof (ServiceMetadataBehavior)) && Array.IndexOf (Description.ServiceType.GetInterfaces (), typeof (IMetadataExchange)) < 0)
199                                         throw new InvalidOperationException (
200                                                 "Add ServiceMetadataBehavior to the ServiceHost to add a endpoint for IMetadataExchange contract.");
201                                         
202                                 ImplementedContracts [ServiceMetadataBehavior.MexContractName] =
203                                         ContractDescription.GetContract (typeof (IMetadataExchange));
204
205                                 foreach (ContractDescription cd in ImplementedContracts.Values)
206                                         if (cd.ContractType == typeof (IMetadataExchange))
207                                                 return cd;
208                                 return null;
209                         }
210
211                         Type type = PopulateType (typeName);
212                         if (type == null)
213                                 return null;
214
215                         foreach (ContractDescription cd in ImplementedContracts.Values) {
216                                 // FIXME: This check is a negative side effect 
217                                 // of the above hack.
218                                 if (cd.ContractType == typeof (IMetadataExchange))
219                                         continue;
220
221                                 if (cd.ContractType == type ||
222                                     cd.ContractType.IsSubclassOf (type) ||
223                                     type.IsInterface && cd.ContractType.GetInterface (type.FullName) == type)
224                                         return cd;
225                         }
226                         return null;
227                 }
228
229                 internal EndpointAddress BuildEndpointAddress (Uri address, Binding binding)
230                 {
231                         if (!address.IsAbsoluteUri) {
232                                 // Find a Base address with matching scheme,
233                                 // and build new absolute address
234                                 if (!base_addresses.Contains (binding.Scheme))
235                                         throw new InvalidOperationException (String.Format ("Could not find base address that matches Scheme {0} for endpoint {1}", binding.Scheme, binding.Name));
236
237                                 Uri baseaddr = base_addresses [binding.Scheme];
238
239                                 if (!baseaddr.AbsoluteUri.EndsWith ("/"))
240                                         baseaddr = new Uri (baseaddr.AbsoluteUri + "/");
241                                 address = new Uri (baseaddr, address);
242                         }
243                         return new EndpointAddress (address);
244                 }
245
246                 internal ServiceEndpoint AddServiceEndpointCore (
247                         ContractDescription cd, Binding binding, EndpointAddress address, Uri listenUri)
248                 {
249                         foreach (ServiceEndpoint e in Description.Endpoints)
250                                 if (e.Contract == cd)
251                                         return e;
252                         ServiceEndpoint se = new ServiceEndpoint (cd, binding, address);
253                         se.ListenUri = listenUri.IsAbsoluteUri ? listenUri : new Uri (address.Uri, listenUri);
254                         Description.Endpoints.Add (se);
255                         return se;
256                 }
257
258                 [MonoTODO]
259                 protected virtual void ApplyConfiguration ()
260                 {
261                         if (Description == null)
262                                 throw new InvalidOperationException ("ApplyConfiguration requires that the Description property be initialized. Either provide a valid ServiceDescription in the CreateDescription method or override the ApplyConfiguration method to provide an alternative implementation");
263
264                         ServiceElement service = GetServiceElement ();
265
266                         //TODO: Should we call here LoadServiceElement ?
267                         if (service != null) {
268                                 
269                                 //base addresses
270                                 HostElement host = service.Host;
271                                 foreach (BaseAddressElement baseAddress in host.BaseAddresses) {
272                                         this.base_addresses.Add (new Uri (baseAddress.BaseAddress));
273                                 }
274
275                                 // services
276                                 foreach (ServiceEndpointElement endpoint in service.Endpoints) {
277                                         // FIXME: consider BindingName as well
278                                         ServiceEndpoint se = AddServiceEndpoint (
279                                                 endpoint.Contract,
280                                                 ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration),
281                                                 endpoint.Address.ToString ());
282                                 }
283                                 // behaviors
284                                 // TODO: use EvaluationContext of ServiceElement.
285                                 ServiceBehaviorElement behavior = ConfigUtil.BehaviorsSection.ServiceBehaviors.Find (service.BehaviorConfiguration);
286                                 if (behavior != null) {
287                                         for (int i = 0; i < behavior.Count; i++) {
288                                                 BehaviorExtensionElement bxel = behavior [i];
289                                                 IServiceBehavior b = (IServiceBehavior) behavior [i].CreateBehavior ();
290                                                 if (b != null)
291                                                         Description.Behaviors.Add (b);
292                                         }
293                                 }
294                         }
295                         // TODO: consider commonBehaviors here
296
297                         // ensure ServiceAuthorizationBehavior
298                         Authorization = Description.Behaviors.Find<ServiceAuthorizationBehavior> ();
299                         if (Authorization == null) {
300                                 Authorization = new ServiceAuthorizationBehavior ();
301                                 Description.Behaviors.Add (Authorization);
302                         }
303
304                         // ensure ServiceDebugBehavior
305                         ServiceDebugBehavior debugBehavior = Description.Behaviors.Find<ServiceDebugBehavior> ();
306                         if (debugBehavior == null) {
307                                 debugBehavior = new ServiceDebugBehavior ();
308                                 Description.Behaviors.Add (debugBehavior);
309                         }
310                 }
311
312                 private ServiceElement GetServiceElement() {
313                         Type serviceType = Description.ServiceType;
314                         if (serviceType == null)
315                                 return null;
316
317                         return ConfigUtil.ServicesSection.Services [serviceType.FullName];                      
318                 }
319
320                 internal ContractDescription GetContract (string name, string ns)
321                 {
322                         foreach (ContractDescription d in ImplementedContracts.Values)
323                                 if (d.Name == name && d.Namespace == ns)
324                                         return d;
325                         return null;
326                 }
327
328                 protected abstract ServiceDescription CreateDescription (
329                         out IDictionary<string,ContractDescription> implementedContracts);
330
331                 protected void InitializeDescription (UriSchemeKeyedCollection baseAddresses)
332                 {
333                         this.base_addresses = baseAddresses;
334                         IDictionary<string,ContractDescription> retContracts;
335                         description = CreateDescription (out retContracts);
336                         contracts = retContracts;
337
338                         ApplyConfiguration ();
339                 }
340
341                 protected virtual void InitializeRuntime ()
342                 {
343                         //First validate the description, which should call all behaviors
344                         //'Validate' method.
345                         ValidateDescription ();
346                         
347                         //Build all ChannelDispatchers, one dispatcher per user configured EndPoint.
348                         //We must keep thet ServiceEndpoints as a seperate collection, since the user
349                         //can change the collection in the description during the behaviors events.
350                         Dictionary<ServiceEndpoint, ChannelDispatcher> endPointToDispatcher = new Dictionary<ServiceEndpoint,ChannelDispatcher>();
351                         ServiceEndpoint[] endPoints = new ServiceEndpoint[Description.Endpoints.Count];
352                         Description.Endpoints.CopyTo (endPoints, 0);
353                         foreach (ServiceEndpoint se in endPoints) {
354                                 //Let all behaviors add their binding parameters
355                                 BindingParameterCollection commonParams =
356                                         new BindingParameterCollection ();
357                                 AddBindingParameters (commonParams, se);
358                                 ChannelDispatcher channel = BuildChannelDispatcher (se, commonParams);
359                                 ChannelDispatchers.Add (channel);                               
360                                 endPointToDispatcher[se] = channel;                             
361                         }
362
363                         //After the ChannelDispatchers are created, and attached to the service host
364                         //Apply dispatching behaviors.
365                         foreach (IServiceBehavior b in Description.Behaviors)
366                                 b.ApplyDispatchBehavior (Description, this);
367
368                         foreach(KeyValuePair<ServiceEndpoint, ChannelDispatcher> val in endPointToDispatcher)
369                                 ApplyDispatchBehavior(val.Value, val.Key);                      
370                 }
371
372                 private void ValidateDescription ()
373                 {
374                         foreach (IServiceBehavior b in Description.Behaviors)
375                                 b.Validate (Description, this);
376                         foreach (ServiceEndpoint endPoint in Description.Endpoints)
377                                 endPoint.Validate ();
378                 }               
379
380                 private void ApplyDispatchBehavior (ChannelDispatcher dispatcher, ServiceEndpoint endPoint) {                   
381                         foreach (IContractBehavior b in endPoint.Contract.Behaviors)
382                                 b.ApplyDispatchBehavior (endPoint.Contract, endPoint, dispatcher.Endpoints[0].DispatchRuntime);
383                         foreach (IEndpointBehavior b in endPoint.Behaviors)
384                                 b.ApplyDispatchBehavior (endPoint, dispatcher.Endpoints [0]);
385                         foreach (OperationDescription operation in endPoint.Contract.Operations) {
386                                 foreach (IOperationBehavior b in operation.Behaviors)
387                                         b.ApplyDispatchBehavior (operation, dispatcher.Endpoints [0].DispatchRuntime.Operations [operation.Name]);
388                         }
389
390                 }
391
392                 internal ChannelDispatcher BuildChannelDispatcher (ServiceEndpoint se, BindingParameterCollection commonParams) {
393
394                         //User the binding parameters to build the channel listener and Dispatcher
395                         IChannelListener lf = BuildListener (se, commonParams);
396                         ChannelDispatcher cd = new ChannelDispatcher (
397                                 lf, se.Binding.Name);
398                         cd.MessageVersion = se.Binding.MessageVersion;
399                         if (cd.MessageVersion == null)
400                                 cd.MessageVersion = MessageVersion.Default;
401
402                         //Attach one EndpointDispacher to the ChannelDispatcher
403                         EndpointDispatcher endpoint_dispatcher =
404                                 new EndpointDispatcher (se.Address, se.Contract.Name, se.Contract.Namespace);
405                         endpoint_dispatcher.DispatchRuntime.Type = Description.ServiceType;
406                         endpoint_dispatcher.ContractFilter = GetContractFilter (se.Contract);
407                         endpoint_dispatcher.ChannelDispatcher = cd;
408                         cd.Endpoints.Add (endpoint_dispatcher);
409                         
410                         //Build the dispatch operations
411                         DispatchRuntime db = endpoint_dispatcher.DispatchRuntime;
412                         foreach (OperationDescription od in se.Contract.Operations)
413                                 if (!db.Operations.Contains (od.Name))
414                                         PopulateDispatchOperation (db, od);
415
416                         return cd;
417                 }
418
419                 private MessageFilter GetContractFilter (ContractDescription contractDescription)
420                 {
421                         List<string> actions = new List<string> ();
422                         foreach (OperationDescription od in contractDescription.Operations)
423                                 foreach (MessageDescription md in od.Messages)
424                                         if (md.Direction == MessageDirection.Input)
425                                                 if (md.Action == "*")
426                                                         return new MatchAllMessageFilter ();
427                                                 else
428                                                         actions.Add (md.Action);
429
430                         return new ActionMessageFilter (actions.ToArray ());
431                 }
432
433                 private void AddBindingParameters (BindingParameterCollection commonParams, ServiceEndpoint endPoint) {
434
435                         commonParams.Add (ChannelProtectionRequirements.CreateFromContract (endPoint.Contract));
436                         foreach (IServiceBehavior b in Description.Behaviors)
437                                 b.AddBindingParameters (Description, this, Description.Endpoints, commonParams);
438
439                         foreach (IContractBehavior b in endPoint.Contract.Behaviors)
440                                 b.AddBindingParameters (endPoint.Contract, endPoint, commonParams);
441                         foreach (IEndpointBehavior b in endPoint.Behaviors)
442                                 b.AddBindingParameters (endPoint, commonParams);
443                         foreach (OperationDescription operation in endPoint.Contract.Operations) {
444                                 foreach (IOperationBehavior b in operation.Behaviors)
445                                         b.AddBindingParameters (operation, commonParams);
446                         }
447                 }
448
449                 void PopulateDispatchOperation (DispatchRuntime db, OperationDescription od) {
450                         string reqA = null, resA = null;
451                         foreach (MessageDescription m in od.Messages) {
452                                 if (m.Direction == MessageDirection.Input)
453                                         reqA = m.Action;
454                                 else
455                                         resA = m.Action;
456                         }
457                         DispatchOperation o =
458                                 od.IsOneWay ?
459                                 new DispatchOperation (db, od.Name, reqA) :
460                                 new DispatchOperation (db, od.Name, reqA, resA);
461                         bool has_void_reply = false;
462                         foreach (MessageDescription md in od.Messages) {
463                                 if (md.Direction == MessageDirection.Input &&
464                                         md.Body.Parts.Count == 1 &&
465                                         md.Body.Parts [0].Type == typeof (Message))
466                                         o.DeserializeRequest = false;
467                                 if (md.Direction == MessageDirection.Output &&
468                                         md.Body.ReturnValue != null) {
469                                         if (md.Body.ReturnValue.Type == typeof (Message))
470                                                 o.SerializeReply = false;
471                                         else if (md.Body.ReturnValue.Type == typeof (void))
472                                                 has_void_reply = true;
473                                 }
474                         }
475
476                         // Setup Invoker
477                         // FIXME: support async method
478                         if (od.SyncMethod != null)
479                                 o.Invoker = new SyncMethodInvoker (od.SyncMethod);
480                         else
481                                 o.Invoker = new AsyncMethodInvoker (od.BeginMethod, od.EndMethod);
482
483                         // Setup Formater
484                         o.Formatter = BaseMessagesFormatter.Create (od);
485
486                         if (o.Action == "*" && o.ReplyAction == "*") {
487                                 //Signature : Message  (Message)
488                                 //          : void  (Message)
489                                 //FIXME: void (IChannel)
490                                 if (!o.DeserializeRequest && (!o.SerializeReply || has_void_reply))
491                                         db.UnhandledDispatchOperation = o;
492                         }
493
494                         db.Operations.Add (o);
495                 }
496
497                 [MonoTODO]
498                 protected void LoadConfigurationSection (ServiceElement element)
499                 {
500                         ServicesSection services = ConfigUtil.ServicesSection;
501                 }
502
503                 void DoOpen (TimeSpan timeout)
504                 {
505                         foreach (var cd in ChannelDispatchers) {
506                                 cd.Open (timeout);
507                                 // This is likely hack.
508                                 if (cd is ChannelDispatcher)
509                                         ((ChannelDispatcher) cd).StartLoop ();
510                         }
511                 }
512
513                 IChannelListener BuildListener (ServiceEndpoint se,
514                         BindingParameterCollection pl)
515                 {
516                         Binding b = se.Binding;
517                         if (b.CanBuildChannelListener<IReplySessionChannel> (pl))
518                                 return b.BuildChannelListener<IReplySessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
519                         if (b.CanBuildChannelListener<IReplyChannel> (pl))
520                                 return b.BuildChannelListener<IReplyChannel> (se.ListenUri, "", se.ListenUriMode, pl);
521                         if (b.CanBuildChannelListener<IInputSessionChannel> (pl))
522                                 return b.BuildChannelListener<IInputSessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
523                         if (b.CanBuildChannelListener<IInputChannel> (pl))
524                                 return b.BuildChannelListener<IInputChannel> (se.ListenUri, "", se.ListenUriMode, pl);
525
526                         if (b.CanBuildChannelListener<IDuplexChannel> (pl))
527                                 return b.BuildChannelListener<IDuplexChannel> (se.ListenUri, "", se.ListenUriMode, pl);
528                         if (b.CanBuildChannelListener<IDuplexSessionChannel> (pl))
529                                 return b.BuildChannelListener<IDuplexSessionChannel> (se.ListenUri, "", se.ListenUriMode, pl);
530                         throw new InvalidOperationException ("None of the listener channel types is supported");
531                 }
532
533                 [MonoTODO]
534                 protected override sealed void OnAbort ()
535                 {
536                 }
537
538                 Action<TimeSpan> close_delegate;
539                 Action<TimeSpan> open_delegate;
540
541                 protected override sealed IAsyncResult OnBeginClose (
542                         TimeSpan timeout, AsyncCallback callback, object state)
543                 {
544                         if (close_delegate != null)
545                                 close_delegate = new Action<TimeSpan> (OnClose);
546                         return close_delegate.BeginInvoke (timeout, callback, state);
547                 }
548
549                 protected override sealed IAsyncResult OnBeginOpen (
550                         TimeSpan timeout, AsyncCallback callback, object state)
551                 {
552                         if (open_delegate == null)
553                                 open_delegate = new Action<TimeSpan> (OnOpen);
554                         return open_delegate.BeginInvoke (timeout, callback, state);
555                 }
556
557                 protected override void OnClose (TimeSpan timeout)
558                 {
559                         DateTime start = DateTime.Now;
560                         ReleasePerformanceCounters ();
561                         List<ChannelDispatcherBase> l = new List<ChannelDispatcherBase> (ChannelDispatchers);
562                         foreach (ChannelDispatcherBase e in l) {
563                                 try {
564                                         TimeSpan ts = timeout - (DateTime.Now - start);
565                                         if (ts < TimeSpan.Zero)
566                                                 e.Abort ();
567                                         else
568                                                 e.Close (ts);
569                                 } catch (Exception ex) {
570                                         Console.WriteLine ("ServiceHostBase failed to close the channel dispatcher:");
571                                         Console.WriteLine (ex);
572                                 }
573                         }
574                 }
575
576                 protected override sealed void OnOpen (TimeSpan timeout)
577                 {
578                         InitializeRuntime ();
579                         DoOpen (timeout);
580                 }
581
582                 protected override void OnEndClose (IAsyncResult result)
583                 {
584                         if (close_delegate == null)
585                                 throw new InvalidOperationException ("Async close operation has not started");
586                         close_delegate.EndInvoke (result);
587                 }
588
589                 protected override sealed void OnEndOpen (IAsyncResult result)
590                 {
591                         if (open_delegate == null)
592                                 throw new InvalidOperationException ("Aync open operation has not started");
593                         open_delegate.EndInvoke (result);
594                 }
595
596                 protected override void OnOpened ()
597                 {
598                 }
599
600                 [MonoTODO]
601                 protected void ReleasePerformanceCounters ()
602                 {
603                 }
604
605                 void IDisposable.Dispose ()
606                 {
607                         Close ();
608                 }
609
610                 class SyncMethodInvoker : IOperationInvoker
611                 {
612                         readonly MethodInfo _methodInfo;
613                         public SyncMethodInvoker (MethodInfo methodInfo) {
614                                 _methodInfo = methodInfo;
615                         }
616                         
617                         #region IOperationInvoker Members
618
619                         public bool IsSynchronous {
620                                 get { return true; }
621                         }
622
623                         public object [] AllocateParameters () {
624                                 return new object [_methodInfo.GetParameters ().Length];
625                         }
626
627                         public object Invoke (object instance, object [] parameters)
628             {
629                                 return _methodInfo.Invoke (instance, parameters);
630                         }
631
632                         public IAsyncResult InvokeBegin (object instance, object [] inputs, AsyncCallback callback, object state) {
633                                 throw new NotSupportedException ();
634                         }
635
636                         public object InvokeEnd (object instance, out object [] outputs, IAsyncResult result) {
637                                 throw new NotSupportedException ();
638                         }
639
640                         #endregion
641                 }
642
643                 class AsyncMethodInvoker : IOperationInvoker
644                 {
645                         readonly MethodInfo _beginMethodInfo, _endMethodInfo;
646                         public AsyncMethodInvoker (MethodInfo beginMethodInfo, MethodInfo endMethodInfo) {
647                                 _beginMethodInfo = beginMethodInfo;
648                                 _endMethodInfo = endMethodInfo;
649                         }
650
651                         #region IOperationInvoker Members
652
653                         public bool IsSynchronous {
654                                 get { return false; }
655                         }
656
657                         public object [] AllocateParameters () {
658                                 return new object [_beginMethodInfo.GetParameters ().Length - 2 + _endMethodInfo.GetParameters().Length-1];
659                         }
660
661                         public object Invoke (object instance, object [] parameters) {
662                                 throw new NotImplementedException ("Can't invoke async method synchronously");
663                                 //BUGBUG: need to differentiate between input and output parameters.
664                                 IAsyncResult asyncResult = InvokeBegin(instance, parameters, delegate(IAsyncResult ignore) { }, null);
665                                 asyncResult.AsyncWaitHandle.WaitOne();
666                                 return InvokeEnd(instance, out parameters, asyncResult);
667                         }
668
669                         public IAsyncResult InvokeBegin (object instance, object [] inputs, AsyncCallback callback, object state) {
670                                 if (inputs.Length + 2 != _beginMethodInfo.GetParameters ().Length)
671                                         throw new ArgumentException ("Wrong number of input parameters");
672                                 object [] fullargs = new object [_beginMethodInfo.GetParameters ().Length];
673                                 Array.Copy (inputs, fullargs, inputs.Length);
674                                 fullargs [inputs.Length] = callback;
675                                 fullargs [inputs.Length + 1] = state;
676                                 return (IAsyncResult) _beginMethodInfo.Invoke (instance, fullargs);
677                         }
678
679                         public object InvokeEnd (object instance, out object [] outputs, IAsyncResult asyncResult) {
680                                 outputs = new object [_endMethodInfo.GetParameters ().Length - 1];
681                                 object [] fullargs = new object [_endMethodInfo.GetParameters ().Length];
682                                 fullargs [outputs.Length] = asyncResult;
683                                 object result = _endMethodInfo.Invoke (instance, fullargs);
684                                 Array.Copy (fullargs, outputs, outputs.Length);
685                                 return result;
686                         }
687
688                         #endregion
689                 }
690         }
691 }