5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2005 Novell, Inc. http://www.novell.com
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System.Collections.Generic;
30 using System.Collections.ObjectModel;
31 using System.ServiceModel.Channels;
32 using System.ServiceModel.Description;
33 using System.ServiceModel.Dispatcher;
34 using System.ServiceModel.Security;
35 using System.Configuration;
36 using System.ServiceModel.Configuration;
39 namespace System.ServiceModel
41 public abstract class ChannelFactory : CommunicationObject,
42 IChannelFactory, ICommunicationObject, IDisposable
46 ServiceEndpoint service_endpoint;
47 IChannelFactory factory;
48 List<IClientChannel> opened_channels = new List<IClientChannel> ();
50 protected ChannelFactory ()
54 internal IChannelFactory OpenedChannelFactory {
56 if (factory == null) {
57 factory = CreateFactory ();
68 internal List<IClientChannel> OpenedChannels {
69 get { return opened_channels; }
72 public ServiceEndpoint Endpoint {
73 get { return service_endpoint; }
76 public ClientCredentials Credentials {
77 get { return Endpoint.Behaviors.Find<ClientCredentials> (); }
80 protected internal override TimeSpan DefaultCloseTimeout {
81 get { return Endpoint.Binding.CloseTimeout; }
84 protected internal override TimeSpan DefaultOpenTimeout {
85 get { return Endpoint.Binding.OpenTimeout; }
88 protected virtual void ApplyConfiguration (string configurationName)
90 if (configurationName == null)
93 #if MOBILE || XAMMAC_4_5
95 // It should automatically use XmlXapResolver
96 var cfg = new SilverlightClientConfigLoader ().Load (XmlReader.Create ("ServiceReferences.ClientConfig"));
98 SilverlightClientConfigLoader.ServiceEndpointConfiguration se = null;
99 if (configurationName == "*")
100 se = cfg.GetServiceEndpointConfiguration (Endpoint.Contract.Name);
102 se = cfg.GetServiceEndpointConfiguration (configurationName);
104 if (se.Binding != null && Endpoint.Binding == null)
105 Endpoint.Binding = se.Binding;
107 Console.WriteLine ("WARNING: Configured binding not found in configuration {0}", configurationName);
108 if (se.Address != null && Endpoint.Address == null)
109 Endpoint.Address = se.Address;
111 Console.WriteLine ("WARNING: Configured endpoint address not found in configuration {0}", configurationName);
112 } catch (Exception) {
114 Console.WriteLine ("WARNING: failed to load endpoint configuration for {0}", configurationName);
118 string contractName = Endpoint.Contract.ConfigurationName;
119 ClientSection client = ConfigUtil.ClientSection;
120 ChannelEndpointElement endpoint = null;
122 foreach (ChannelEndpointElement el in client.Endpoints) {
123 if (el.Contract == contractName && (configurationName == el.Name || configurationName == "*")) {
124 if (endpoint != null)
125 throw new InvalidOperationException (String.Format ("More then one endpoint matching contract {0} was found.", contractName));
130 if (endpoint == null)
131 throw new InvalidOperationException (String.Format ("Client endpoint configuration '{0}' was not found in {1} endpoints.", configurationName, client.Endpoints.Count));
133 var binding = String.IsNullOrEmpty (endpoint.Binding) ? null : ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration);
134 var contractType = ConfigUtil.GetTypeFromConfigString (endpoint.Contract, NamedConfigCategory.Contract);
135 if (contractType == null)
136 throw new ArgumentException (String.Format ("Contract '{0}' was not found", endpoint.Contract));
137 var contract = String.IsNullOrEmpty (endpoint.Contract) ? Endpoint.Contract : ContractDescription.GetContract (contractType);
139 if (!String.IsNullOrEmpty (endpoint.Kind)) {
140 var se = ConfigUtil.ConfigureStandardEndpoint (contract, endpoint);
141 if (se.Binding == null)
142 se.Binding = binding;
143 if (se.Address == null && se.Binding != null) // standard endpoint might have empty address
144 se.Address = new EndpointAddress (endpoint.Address);
145 if (se.Binding == null && se.Address != null) // look for protocol mapping
146 se.Binding = ConfigUtil.GetBindingByProtocolMapping (se.Address.Uri);
148 service_endpoint = se;
150 if (binding == null && endpoint.Address != null) // look for protocol mapping
151 Endpoint.Binding = ConfigUtil.GetBindingByProtocolMapping (endpoint.Address);
153 if (Endpoint.Binding == null)
154 Endpoint.Binding = ConfigUtil.CreateBinding (endpoint.Binding, endpoint.BindingConfiguration);
155 if (Endpoint.Address == null)
156 Endpoint.Address = new EndpointAddress (endpoint.Address);
158 if (endpoint.BehaviorConfiguration != "")
159 ApplyBehavior (endpoint.BehaviorConfiguration);
163 #if !MOBILE && !XAMMAC_4_5
164 private void ApplyBehavior (string behaviorConfig)
166 BehaviorsSection behaviorsSection = ConfigUtil.BehaviorsSection;
167 EndpointBehaviorElement behaviorElement = behaviorsSection.EndpointBehaviors [behaviorConfig];
169 foreach (BehaviorExtensionElement el in behaviorElement) {
170 IEndpointBehavior behavior = (IEndpointBehavior) el.CreateBehavior ();
171 Endpoint.Behaviors.Remove (behavior.GetType ());
172 Endpoint.Behaviors.Add (behavior);
177 protected virtual IChannelFactory CreateFactory ()
179 bool isOneWay = true; // check OperationDescription.IsOneWay
180 foreach (var od in Endpoint.Contract.Operations)
186 BindingParameterCollection pl = CreateBindingParameters ();
188 // the assumption on the type of created channel could
189 // be wrong, but would mostly fit the actual
190 // requirements. No books have explained how it is done.
193 switch (Endpoint.Contract.SessionMode) {
194 case SessionMode.Required:
195 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexSessionChannel> (pl))
196 return Endpoint.Binding.BuildChannelFactory<IDuplexSessionChannel> (pl);
198 case SessionMode.Allowed:
199 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexChannel> (pl))
200 return Endpoint.Binding.BuildChannelFactory<IDuplexChannel> (pl);
201 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexSessionChannel> (pl))
202 return Endpoint.Binding.BuildChannelFactory<IDuplexSessionChannel> (pl);
205 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexChannel> (pl))
206 return Endpoint.Binding.BuildChannelFactory<IDuplexChannel> (pl);
210 if (Endpoint.Contract.CallbackContractType != null)
211 throw new InvalidOperationException ("The binding does not support duplex channel types that the contract requies for CallbackContractType.");
214 switch (Endpoint.Contract.SessionMode) {
215 case SessionMode.Required:
216 if (Endpoint.Binding.CanBuildChannelFactory<IOutputSessionChannel> (pl))
217 return Endpoint.Binding.BuildChannelFactory<IOutputSessionChannel> (pl);
218 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexSessionChannel> (pl))
219 return Endpoint.Binding.BuildChannelFactory<IDuplexSessionChannel> (pl);
221 case SessionMode.Allowed:
222 if (Endpoint.Binding.CanBuildChannelFactory<IOutputChannel> (pl))
223 return Endpoint.Binding.BuildChannelFactory<IOutputChannel> (pl);
224 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexChannel> (pl))
225 return Endpoint.Binding.BuildChannelFactory<IDuplexChannel> (pl);
226 goto case SessionMode.Required;
228 if (Endpoint.Binding.CanBuildChannelFactory<IOutputChannel> (pl))
229 return Endpoint.Binding.BuildChannelFactory<IOutputChannel> (pl);
230 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexChannel> (pl))
231 return Endpoint.Binding.BuildChannelFactory<IDuplexChannel> (pl);
235 // both OneWay and non-OneWay contracts fall into here.
237 switch (Endpoint.Contract.SessionMode) {
238 case SessionMode.Required:
239 if (Endpoint.Binding.CanBuildChannelFactory<IRequestSessionChannel> (pl))
240 return Endpoint.Binding.BuildChannelFactory<IRequestSessionChannel> (pl);
242 case SessionMode.Allowed:
243 if (Endpoint.Binding.CanBuildChannelFactory<IRequestChannel> (pl))
244 return Endpoint.Binding.BuildChannelFactory<IRequestChannel> (pl);
245 if (Endpoint.Binding.CanBuildChannelFactory<IRequestSessionChannel> (pl))
246 return Endpoint.Binding.BuildChannelFactory<IRequestSessionChannel> (pl);
249 if (Endpoint.Binding.CanBuildChannelFactory<IRequestChannel> (pl))
250 return Endpoint.Binding.BuildChannelFactory<IRequestChannel> (pl);
254 throw new InvalidOperationException (String.Format ("The binding does not support any of the channel types that the contract '{0}' allows.", Endpoint.Contract.Name));
257 BindingParameterCollection CreateBindingParameters ()
259 BindingParameterCollection pl =
260 new BindingParameterCollection ();
262 ContractDescription cd = Endpoint.Contract;
264 pl.Add (ChannelProtectionRequirements.CreateFromContract (cd));
267 foreach (IEndpointBehavior behavior in Endpoint.Behaviors)
268 behavior.AddBindingParameters (Endpoint, pl);
273 protected abstract ServiceEndpoint CreateDescription ();
275 void IDisposable.Dispose ()
280 public T GetProperty<T> () where T : class
282 if (OpenedChannelFactory != null)
283 return OpenedChannelFactory.GetProperty<T> ();
287 protected void EnsureOpened ()
289 if (Endpoint == null)
290 throw new InvalidOperationException ("A service endpoint must be configured for this channel factory");
291 if (Endpoint.Contract == null)
292 throw new InvalidOperationException ("A service Contract must be configured for this channel factory");
293 if (Endpoint.Binding == null)
294 throw new InvalidOperationException ("A Binding must be configured for this channel factory");
296 if (State != CommunicationState.Opened)
300 protected void InitializeEndpoint (
301 string configurationName,
302 EndpointAddress remoteAddress)
304 InitializeEndpoint (CreateDescription ());
305 if (remoteAddress != null)
306 service_endpoint.Address = remoteAddress;
307 ApplyConfiguration (configurationName);
310 protected void InitializeEndpoint (Binding binding,
311 EndpointAddress address)
313 InitializeEndpoint (CreateDescription ());
315 service_endpoint.Binding = binding;
317 service_endpoint.Address = address;
320 protected void InitializeEndpoint (ServiceEndpoint endpoint)
322 if (endpoint == null)
323 throw new ArgumentNullException ("endpoint");
324 service_endpoint = endpoint;
327 protected override void OnAbort ()
329 if (OpenedChannelFactory != null)
330 OpenedChannelFactory.Abort ();
333 Action<TimeSpan> close_delegate;
334 Action<TimeSpan> open_delegate;
337 protected override IAsyncResult OnBeginClose (
338 TimeSpan timeout, AsyncCallback callback, object state)
340 if (close_delegate == null)
341 close_delegate = new Action<TimeSpan> (OnClose);
342 return close_delegate.BeginInvoke (timeout, callback, state);
345 protected override IAsyncResult OnBeginOpen (
346 TimeSpan timeout, AsyncCallback callback, object state)
348 if (open_delegate == null)
349 open_delegate = new Action<TimeSpan> (OnClose);
350 return open_delegate.BeginInvoke (timeout, callback, state);
353 protected override void OnEndClose (IAsyncResult result)
355 if (close_delegate == null)
356 throw new InvalidOperationException ("Async close operation has not started");
357 close_delegate.EndInvoke (result);
360 protected override void OnEndOpen (IAsyncResult result)
362 if (open_delegate == null)
363 throw new InvalidOperationException ("Async close operation has not started");
364 open_delegate.EndInvoke (result);
367 protected override void OnClose (TimeSpan timeout)
369 DateTime start = DateTime.Now;
370 foreach (var ch in opened_channels.ToArray ())
371 ch.Close (timeout - (DateTime.Now - start));
372 if (OpenedChannelFactory != null)
373 OpenedChannelFactory.Close (timeout - (DateTime.Now - start));
376 protected override void OnOpen (TimeSpan timeout)
380 protected override void OnOpening ()
383 OpenedChannelFactory = CreateFactory ();
386 protected override void OnOpened ()
389 OpenedChannelFactory.Open ();
395 interface UninitializedContract
398 void ItShouldReallyGone ();