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 endpointConfig)
90 if (endpointConfig == null)
95 // It should automatically use XmlXapResolver
96 var cfg = new SilverlightClientConfigLoader ().Load (XmlReader.Create ("ServiceReferences.ClientConfig"));
98 SilverlightClientConfigLoader.ServiceEndpointConfiguration se = null;
99 if (endpointConfig == "*")
100 se = cfg.GetServiceEndpointConfiguration (Endpoint.Contract.Name);
102 se = cfg.GetServiceEndpointConfiguration (endpointConfig);
104 if (se.Binding != null && Endpoint.Binding == null)
105 Endpoint.Binding = se.Binding;
107 Console.WriteLine ("WARNING: Configured binding not found in configuration {0}", endpointConfig);
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}", endpointConfig);
112 } catch (Exception) {
114 Console.WriteLine ("WARNING: failed to load endpoint configuration for {0}", endpointConfig);
118 string contractName = Endpoint.Contract.ConfigurationName;
119 ClientSection client = ConfigUtil.ClientSection;
120 ChannelEndpointElement res = null;
121 foreach (ChannelEndpointElement el in client.Endpoints) {
122 if (el.Contract == contractName && (endpointConfig == el.Name || endpointConfig == "*")) {
124 throw new InvalidOperationException (String.Format ("More then one endpoint matching contract {0} was found.", contractName));
130 throw new InvalidOperationException (String.Format ("Client endpoint configuration '{0}' was not found in {1} endpoints.", endpointConfig, client.Endpoints.Count));
132 if (Endpoint.Binding == null)
133 Endpoint.Binding = ConfigUtil.CreateBinding (res.Binding, res.BindingConfiguration);
134 if (Endpoint.Address == null)
135 Endpoint.Address = new EndpointAddress (res.Address);
137 if (res.BehaviorConfiguration != "")
138 ApplyBehavior (res.BehaviorConfiguration);
143 private void ApplyBehavior (string behaviorConfig)
145 BehaviorsSection behaviorsSection = ConfigUtil.BehaviorsSection;
146 EndpointBehaviorElement behaviorElement = behaviorsSection.EndpointBehaviors [behaviorConfig];
148 foreach (BehaviorExtensionElement el in behaviorElement) {
149 IEndpointBehavior behavior = (IEndpointBehavior) el.CreateBehavior ();
150 Endpoint.Behaviors.Remove (behavior.GetType ());
151 Endpoint.Behaviors.Add (behavior);
156 protected virtual IChannelFactory CreateFactory ()
158 bool isOneWay = true; // check OperationDescription.IsOneWay
159 foreach (var od in Endpoint.Contract.Operations)
165 BindingParameterCollection pl = CreateBindingParameters ();
167 // the assumption on the type of created channel could
168 // be wrong, but would mostly fit the actual
169 // requirements. No books have explained how it is done.
172 switch (Endpoint.Contract.SessionMode) {
173 case SessionMode.Required:
174 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexSessionChannel> (pl))
175 return Endpoint.Binding.BuildChannelFactory<IDuplexSessionChannel> (pl);
177 case SessionMode.Allowed:
178 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexChannel> (pl))
179 return Endpoint.Binding.BuildChannelFactory<IDuplexChannel> (pl);
180 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexSessionChannel> (pl))
181 return Endpoint.Binding.BuildChannelFactory<IDuplexSessionChannel> (pl);
184 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexChannel> (pl))
185 return Endpoint.Binding.BuildChannelFactory<IDuplexChannel> (pl);
189 if (Endpoint.Contract.CallbackContractType != null)
190 throw new InvalidOperationException ("The binding does not support duplex channel types that the contract requies for CallbackContractType.");
193 switch (Endpoint.Contract.SessionMode) {
194 case SessionMode.Required:
195 if (Endpoint.Binding.CanBuildChannelFactory<IOutputSessionChannel> (pl))
196 return Endpoint.Binding.BuildChannelFactory<IOutputSessionChannel> (pl);
197 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexSessionChannel> (pl))
198 return Endpoint.Binding.BuildChannelFactory<IDuplexSessionChannel> (pl);
200 case SessionMode.Allowed:
201 if (Endpoint.Binding.CanBuildChannelFactory<IOutputChannel> (pl))
202 return Endpoint.Binding.BuildChannelFactory<IOutputChannel> (pl);
203 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexChannel> (pl))
204 return Endpoint.Binding.BuildChannelFactory<IDuplexChannel> (pl);
205 goto case SessionMode.Required;
207 if (Endpoint.Binding.CanBuildChannelFactory<IOutputChannel> (pl))
208 return Endpoint.Binding.BuildChannelFactory<IOutputChannel> (pl);
209 if (Endpoint.Binding.CanBuildChannelFactory<IDuplexChannel> (pl))
210 return Endpoint.Binding.BuildChannelFactory<IDuplexChannel> (pl);
214 // both OneWay and non-OneWay contracts fall into here.
216 switch (Endpoint.Contract.SessionMode) {
217 case SessionMode.Required:
218 if (Endpoint.Binding.CanBuildChannelFactory<IRequestSessionChannel> (pl))
219 return Endpoint.Binding.BuildChannelFactory<IRequestSessionChannel> (pl);
221 case SessionMode.Allowed:
222 if (Endpoint.Binding.CanBuildChannelFactory<IRequestChannel> (pl))
223 return Endpoint.Binding.BuildChannelFactory<IRequestChannel> (pl);
224 if (Endpoint.Binding.CanBuildChannelFactory<IRequestSessionChannel> (pl))
225 return Endpoint.Binding.BuildChannelFactory<IRequestSessionChannel> (pl);
228 if (Endpoint.Binding.CanBuildChannelFactory<IRequestChannel> (pl))
229 return Endpoint.Binding.BuildChannelFactory<IRequestChannel> (pl);
233 throw new InvalidOperationException (String.Format ("The binding does not support any of the channel types that the contract '{0}' allows.", Endpoint.Contract.Name));
236 BindingParameterCollection CreateBindingParameters ()
238 BindingParameterCollection pl =
239 new BindingParameterCollection ();
241 ContractDescription cd = Endpoint.Contract;
243 pl.Add (ChannelProtectionRequirements.CreateFromContract (cd));
245 foreach (IEndpointBehavior behavior in Endpoint.Behaviors)
246 behavior.AddBindingParameters (Endpoint, pl);
252 protected abstract ServiceEndpoint CreateDescription ();
254 void IDisposable.Dispose ()
259 public T GetProperty<T> () where T : class
261 if (OpenedChannelFactory != null)
262 OpenedChannelFactory.GetProperty<T> ();
266 protected void EnsureOpened ()
268 if (Endpoint == null)
269 throw new InvalidOperationException ("A service endpoint must be configured for this channel factory");
270 if (Endpoint.Address == null)
271 throw new InvalidOperationException ("An EndpointAddress must be configured for this channel factory");
272 if (Endpoint.Contract == null)
273 throw new InvalidOperationException ("A service Contract must be configured for this channel factory");
274 if (Endpoint.Binding == null)
275 throw new InvalidOperationException ("A Binding must be configured for this channel factory");
277 if (State != CommunicationState.Opened)
281 protected void InitializeEndpoint (
282 string endpointConfigurationName,
283 EndpointAddress remoteAddress)
285 InitializeEndpoint (CreateDescription ());
286 if (remoteAddress != null)
287 service_endpoint.Address = remoteAddress;
288 ApplyConfiguration (endpointConfigurationName);
291 protected void InitializeEndpoint (Binding binding,
292 EndpointAddress remoteAddress)
294 InitializeEndpoint (CreateDescription ());
296 service_endpoint.Binding = binding;
297 if (remoteAddress != null)
298 service_endpoint.Address = remoteAddress;
301 protected void InitializeEndpoint (ServiceEndpoint endpoint)
303 if (endpoint == null)
304 throw new ArgumentNullException ("endpoint");
305 service_endpoint = endpoint;
308 protected override void OnAbort ()
310 if (OpenedChannelFactory != null)
311 OpenedChannelFactory.Abort ();
314 Action<TimeSpan> close_delegate;
315 Action<TimeSpan> open_delegate;
318 protected override IAsyncResult OnBeginClose (
319 TimeSpan timeout, AsyncCallback callback, object state)
321 if (close_delegate == null)
322 close_delegate = new Action<TimeSpan> (OnClose);
323 return close_delegate.BeginInvoke (timeout, callback, state);
326 protected override IAsyncResult OnBeginOpen (
327 TimeSpan timeout, AsyncCallback callback, object state)
329 if (open_delegate == null)
330 open_delegate = new Action<TimeSpan> (OnClose);
331 return open_delegate.BeginInvoke (timeout, callback, state);
334 protected override void OnEndClose (IAsyncResult result)
336 if (close_delegate == null)
337 throw new InvalidOperationException ("Async close operation has not started");
338 close_delegate.EndInvoke (result);
341 protected override void OnEndOpen (IAsyncResult result)
343 if (open_delegate == null)
344 throw new InvalidOperationException ("Async close operation has not started");
345 open_delegate.EndInvoke (result);
348 protected override void OnClose (TimeSpan timeout)
350 DateTime start = DateTime.Now;
351 foreach (var ch in opened_channels.ToArray ())
352 ch.Close (timeout - (DateTime.Now - start));
353 if (OpenedChannelFactory != null)
354 OpenedChannelFactory.Close (timeout - (DateTime.Now - start));
357 protected override void OnOpen (TimeSpan timeout)
361 protected override void OnOpening ()
364 OpenedChannelFactory = CreateFactory ();
367 protected override void OnOpened ()
370 OpenedChannelFactory.Open ();
376 interface UninitializedContract
379 void ItShouldReallyGone ();