X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.ServiceModel%2FSystem.ServiceModel%2FServiceHostBase.cs;h=52bb1ed723e0d72aed7641f7267d62829f39bb0e;hb=c862c450594ac1652928195401f63350bcc51a10;hp=bba866c506e481e343fa08a6a4d13b6521e42009;hpb=1e047697780f57e5d71b3e8ab5c51186ae599c9f;p=mono.git diff --git a/mcs/class/System.ServiceModel/System.ServiceModel/ServiceHostBase.cs b/mcs/class/System.ServiceModel/System.ServiceModel/ServiceHostBase.cs index bba866c506e..52bb1ed723e 100644 --- a/mcs/class/System.ServiceModel/System.ServiceModel/ServiceHostBase.cs +++ b/mcs/class/System.ServiceModel/System.ServiceModel/ServiceHostBase.cs @@ -35,17 +35,20 @@ using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.ServiceModel.Security; using System.Reflection; +using System.Threading; namespace System.ServiceModel { public abstract partial class ServiceHostBase : CommunicationObject, IExtensibleObject, IDisposable { + // It is used for mapping a ServiceHostBase to HttpChannelListener precisely. + internal static ServiceHostBase CurrentServiceHostHack; + ServiceCredentials credentials; ServiceDescription description; UriSchemeKeyedCollection base_addresses; TimeSpan open_timeout, close_timeout, instance_idle_timeout; - ServiceThrottle throttle; List contexts; ReadOnlyCollection exposed_contexts; ChannelDispatcherCollection channel_dispatchers; @@ -59,7 +62,6 @@ namespace System.ServiceModel close_timeout = DefaultCloseTimeout; credentials = new ServiceCredentials (); - throttle = new ServiceThrottle (); contexts = new List (); exposed_contexts = new ReadOnlyCollection (contexts); channel_dispatchers = new ChannelDispatcherCollection (this); @@ -116,7 +118,6 @@ namespace System.ServiceModel private set; } - [MonoTODO] public ServiceCredentials Credentials { get { return credentials; } } @@ -129,7 +130,6 @@ namespace System.ServiceModel get { return contracts; } } - [MonoTODO] public IExtensionCollection Extensions { get { if (extensions == null) @@ -189,20 +189,39 @@ namespace System.ServiceModel string implementedContract, Binding binding, Uri address) { - return AddServiceEndpoint (implementedContract, binding, address, address); + return AddServiceEndpoint (implementedContract, binding, address, null); } public ServiceEndpoint AddServiceEndpoint ( string implementedContract, Binding binding, Uri address, Uri listenUri) { - EndpointAddress ea = BuildEndpointAddress (address, binding); + EndpointAddress ea = new EndpointAddress (BuildAbsoluteUri (address, binding)); ContractDescription cd = GetContract (implementedContract, binding.Namespace == "http://schemas.microsoft.com/ws/2005/02/mex/bindings"); if (cd == null) throw new InvalidOperationException (String.Format ("Contract '{0}' was not found in the implemented contracts in this service host.", implementedContract)); return AddServiceEndpointCore (cd, binding, ea, listenUri); } +#if NET_4_0 + public virtual void AddServiceEndpoint (ServiceEndpoint endpoint) + { + if (endpoint == null) + throw new ArgumentNullException ("endpoint"); + + ThrowIfDisposedOrImmutable (); + + if (endpoint.Address == null) + throw new ArgumentException ("Address on the argument endpoint is null"); + if (endpoint.Contract == null) + throw new ArgumentException ("Contract on the argument endpoint is null"); + if (endpoint.Binding == null) + throw new ArgumentException ("Binding on the argument endpoint is null"); + + Description.Endpoints.Add (endpoint); + } +#endif + Type PopulateType (string typeName) { Type type = Type.GetType (typeName); @@ -258,7 +277,7 @@ namespace System.ServiceModel return null; } - internal EndpointAddress BuildEndpointAddress (Uri address, Binding binding) + internal Uri BuildAbsoluteUri (Uri address, Binding binding) { if (!address.IsAbsoluteUri) { // Find a Base address with matching scheme, @@ -272,17 +291,21 @@ namespace System.ServiceModel baseaddr = new Uri (baseaddr.AbsoluteUri + "/"); address = new Uri (baseaddr, address); } - return new EndpointAddress (address); + return address; } internal ServiceEndpoint AddServiceEndpointCore ( ContractDescription cd, Binding binding, EndpointAddress address, Uri listenUri) { + if (listenUri != null) + listenUri = BuildAbsoluteUri (listenUri, binding); + foreach (ServiceEndpoint e in Description.Endpoints) if (e.Contract == cd && e.Binding == binding && e.Address == address && e.ListenUri.Equals (listenUri)) return e; ServiceEndpoint se = new ServiceEndpoint (cd, binding, address); - se.ListenUri = listenUri.IsAbsoluteUri ? listenUri : new Uri (address.Uri, listenUri); + // FIXME: should we reject relative ListenUri? + se.ListenUri = listenUri ?? address.Uri; Description.Endpoints.Add (se); return se; } @@ -293,8 +316,6 @@ namespace System.ServiceModel 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"); ServiceElement service = GetServiceElement (); - - //TODO: Should we call here LoadServiceElement ? if (service != null) { //base addresses @@ -304,7 +325,6 @@ namespace System.ServiceModel } // behaviors - // TODO: use EvaluationContext of ServiceElement. ServiceBehaviorElement behavior = ConfigUtil.BehaviorsSection.ServiceBehaviors [service.BehaviorConfiguration]; if (behavior != null) { foreach (var bxe in behavior) { @@ -312,13 +332,15 @@ namespace System.ServiceModel Description.Behaviors.Add (b); } } + else + throw new ArgumentException (String.Format ("Service behavior {0} is specified, but was not found", service.BehaviorConfiguration)); // services foreach (ServiceEndpointElement endpoint in service.Endpoints) { ServiceEndpoint se = AddServiceEndpoint ( endpoint.Contract, ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration), - endpoint.Address.ToString ()); + endpoint.Address); // endpoint behaviors EndpointBehaviorElement epbehavior = ConfigUtil.BehaviorsSection.EndpointBehaviors [endpoint.BehaviorConfiguration]; if (epbehavior != null) @@ -375,28 +397,36 @@ namespace System.ServiceModel //Build all ChannelDispatchers, one dispatcher per user configured EndPoint. //We must keep thet ServiceEndpoints as a seperate collection, since the user //can change the collection in the description during the behaviors events. - Dictionary endPointToDispatcher = new Dictionary(); ServiceEndpoint[] endPoints = new ServiceEndpoint[Description.Endpoints.Count]; Description.Endpoints.CopyTo (endPoints, 0); + var builder = new DispatcherBuilder (this); foreach (ServiceEndpoint se in endPoints) { - var commonParams = new BindingParameterCollection (); foreach (IServiceBehavior b in Description.Behaviors) b.AddBindingParameters (Description, this, Description.Endpoints, commonParams); - var channel = new DispatcherBuilder ().BuildChannelDispatcher (Description.ServiceType, se, commonParams); - ChannelDispatchers.Add (channel); - endPointToDispatcher[se] = channel; + var channel = builder.BuildChannelDispatcher (Description.ServiceType, se, commonParams); + if (!ChannelDispatchers.Contains (channel)) + ChannelDispatchers.Add (channel); } //After the ChannelDispatchers are created, and attached to the service host //Apply dispatching behaviors. + // + // This behavior application order is tricky: first only + // ServiceDebugBehavior and ServiceMetadataBehavior are + // applied, and then other service behaviors are applied. + // It is because those two behaviors adds ChannelDispatchers + // and any other service behaviors must be applied to + // those newly populated dispatchers. + foreach (IServiceBehavior b in Description.Behaviors) + if (b is ServiceMetadataBehavior || b is ServiceDebugBehavior) + b.ApplyDispatchBehavior (Description, this); foreach (IServiceBehavior b in Description.Behaviors) - b.ApplyDispatchBehavior (Description, this); + if (!(b is ServiceMetadataBehavior || b is ServiceDebugBehavior)) + b.ApplyDispatchBehavior (Description, this); - foreach(KeyValuePair val in endPointToDispatcher) - foreach (var ed in val.Value.Endpoints) - ApplyDispatchBehavior (ed, val.Key); + builder.ApplyDispatchBehaviors (); } private void ValidateDescription () @@ -410,19 +440,6 @@ namespace System.ServiceModel throw new InvalidOperationException ("The ServiceHost must have at least one application endpoint (that does not include metadata exchange contract) defined by either configuration, behaviors or call to AddServiceEndpoint methods."); } - private void ApplyDispatchBehavior (EndpointDispatcher ed, ServiceEndpoint endPoint) - { - foreach (IContractBehavior b in endPoint.Contract.Behaviors) - b.ApplyDispatchBehavior (endPoint.Contract, endPoint, ed.DispatchRuntime); - foreach (IEndpointBehavior b in endPoint.Behaviors) - b.ApplyDispatchBehavior (endPoint, ed); - foreach (OperationDescription operation in endPoint.Contract.Operations) { - foreach (IOperationBehavior b in operation.Behaviors) - b.ApplyDispatchBehavior (operation, ed.DispatchRuntime.Operations [operation.Name]); - } - - } - [MonoTODO] protected void LoadConfigurationSection (ServiceElement element) { @@ -476,11 +493,27 @@ namespace System.ServiceModel { DateTime start = DateTime.Now; InitializeRuntime (); - foreach (var cd in ChannelDispatchers) + for (int i = 0; i < ChannelDispatchers.Count; i++) { + // Skip ServiceMetadataExtension-based one. special case. + for (int j = i + 1; j < ChannelDispatchers.Count; j++) { + var cd1 = ChannelDispatchers [i]; + var cd2 = ChannelDispatchers [j]; + if (cd1.IsMex || cd2.IsMex) + continue; + if (cd1.Listener.Uri.Equals (cd2.Listener.Uri)) + throw new InvalidOperationException ("Two or more service endpoints with different Binding instance are bound to the same listen URI."); + } + } + + var waits = new List (); + foreach (var cd in ChannelDispatchers) { + var wait = new ManualResetEvent (false); + cd.Opened += delegate { wait.Set (); }; + waits.Add (wait); cd.Open (timeout - (DateTime.Now - start)); + } - // FIXME: remove this hack. It should make sure that each ChannelDispatcher's loop has started, using WaitHandle.WaitAll() or something similar. - System.Threading.Thread.Sleep (300); + WaitHandle.WaitAll (waits.ToArray ()); } protected override void OnEndClose (IAsyncResult result) @@ -511,104 +544,82 @@ namespace System.ServiceModel { Close (); } + } - /* - class SyncMethodInvoker : IOperationInvoker - { - readonly MethodInfo _methodInfo; - public SyncMethodInvoker (MethodInfo methodInfo) { - _methodInfo = methodInfo; - } - - #region IOperationInvoker Members - - public bool IsSynchronous { - get { return true; } - } - - public object [] AllocateParameters () { - return new object [_methodInfo.GetParameters ().Length]; - } - - public object Invoke (object instance, object [] parameters) - { - return _methodInfo.Invoke (instance, parameters); - } - - public IAsyncResult InvokeBegin (object instance, object [] inputs, AsyncCallback callback, object state) { - throw new NotSupportedException (); - } - - public object InvokeEnd (object instance, out object [] outputs, IAsyncResult result) { - throw new NotSupportedException (); - } - - #endregion - } + /// + /// Builds ChannelDispatchers as appropriate to service the service endpoints. + /// + partial class DispatcherBuilder + { + ServiceHostBase host; - class AsyncMethodInvoker : IOperationInvoker + public DispatcherBuilder (ServiceHostBase host) { - readonly MethodInfo _beginMethodInfo, _endMethodInfo; - public AsyncMethodInvoker (MethodInfo beginMethodInfo, MethodInfo endMethodInfo) { - _beginMethodInfo = beginMethodInfo; - _endMethodInfo = endMethodInfo; - } - - #region IOperationInvoker Members - - public bool IsSynchronous { - get { return false; } - } - - public object [] AllocateParameters () { - return new object [_beginMethodInfo.GetParameters ().Length - 2 + _endMethodInfo.GetParameters().Length-1]; - } - - public object Invoke (object instance, object [] parameters) { - throw new NotImplementedException ("Can't invoke async method synchronously"); - //BUGBUG: need to differentiate between input and output parameters. - IAsyncResult asyncResult = InvokeBegin(instance, parameters, delegate(IAsyncResult ignore) { }, null); - asyncResult.AsyncWaitHandle.WaitOne(); - return InvokeEnd(instance, out parameters, asyncResult); - } - - public IAsyncResult InvokeBegin (object instance, object [] inputs, AsyncCallback callback, object state) { - if (inputs.Length + 2 != _beginMethodInfo.GetParameters ().Length) - throw new ArgumentException ("Wrong number of input parameters"); - object [] fullargs = new object [_beginMethodInfo.GetParameters ().Length]; - Array.Copy (inputs, fullargs, inputs.Length); - fullargs [inputs.Length] = callback; - fullargs [inputs.Length + 1] = state; - return (IAsyncResult) _beginMethodInfo.Invoke (instance, fullargs); - } + this.host = host; + } - public object InvokeEnd (object instance, out object [] outputs, IAsyncResult asyncResult) { - outputs = new object [_endMethodInfo.GetParameters ().Length - 1]; - object [] fullargs = new object [_endMethodInfo.GetParameters ().Length]; - fullargs [outputs.Length] = asyncResult; - object result = _endMethodInfo.Invoke (instance, fullargs); - Array.Copy (fullargs, outputs, outputs.Length); - return result; - } + Dictionary built_dispatchers = new Dictionary (); + Dictionary ep_to_dispatcher_ep = new Dictionary (); - #endregion - } - */ - } + internal static Action ChannelDispatcherSetter; - partial class DispatcherBuilder - { internal ChannelDispatcher BuildChannelDispatcher (Type serviceType, ServiceEndpoint se, BindingParameterCollection commonParams) { //Let all behaviors add their binding parameters AddBindingParameters (commonParams, se); - //User the binding parameters to build the channel listener and Dispatcher - IChannelListener lf = BuildListener (se, commonParams); - ChannelDispatcher cd = new ChannelDispatcher ( - lf, se.Binding.Name); - cd.InitializeServiceEndpoint (serviceType, se); + + // See if there's an existing channel that matches this endpoint + var version = se.Binding.GetProperty (commonParams); + if (version == null) + throw new InvalidOperationException ("At least one BindingElement in the Binding must override GetProperty method to return a MessageVersion and no prior binding element should return null instead of calling GetInnerProperty method on BindingContext."); + + ChannelDispatcher cd = FindExistingDispatcher (se); + EndpointDispatcher ep; + if (cd != null) { + ep = cd.InitializeServiceEndpoint (serviceType, se); + } else { + // Use the binding parameters to build the channel listener and Dispatcher. + lock (HttpTransportBindingElement.ListenerBuildLock) { + ServiceHostBase.CurrentServiceHostHack = host; + IChannelListener lf = BuildListener (se, commonParams); + cd = new ChannelDispatcher (lf, se.Binding.Name); + cd.MessageVersion = version; + if (ChannelDispatcherSetter != null) { + ChannelDispatcherSetter (cd); + ChannelDispatcherSetter = null; + } + ServiceHostBase.CurrentServiceHostHack = null; + } + ep = cd.InitializeServiceEndpoint (serviceType, se); + built_dispatchers.Add (se.Binding, cd); + } + ep_to_dispatcher_ep[se] = ep; return cd; } + + ChannelDispatcher FindExistingDispatcher (ServiceEndpoint se) + { + return built_dispatchers.FirstOrDefault ((KeyValuePair p) => se.Binding == p.Key).Value; + } + + internal void ApplyDispatchBehaviors () + { + foreach (KeyValuePair val in ep_to_dispatcher_ep) + ApplyDispatchBehavior (val.Value, val.Key); + } + + private void ApplyDispatchBehavior (EndpointDispatcher ed, ServiceEndpoint endPoint) + { + foreach (IContractBehavior b in endPoint.Contract.Behaviors) + b.ApplyDispatchBehavior (endPoint.Contract, endPoint, ed.DispatchRuntime); + foreach (IEndpointBehavior b in endPoint.Behaviors) + b.ApplyDispatchBehavior (endPoint, ed); + foreach (OperationDescription operation in endPoint.Contract.Operations) { + foreach (IOperationBehavior b in operation.Behaviors) + b.ApplyDispatchBehavior (operation, ed.DispatchRuntime.Operations [operation.Name]); + } + + } private void AddBindingParameters (BindingParameterCollection commonParams, ServiceEndpoint endPoint) {