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