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