// // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //====================================================================================================== // Code originally contributed by Microsoft Corporation. // This contribution to the Stonehenge project is limited strictly // to the source code that is submitted in this submission. // Any technology, including underlying platform technology, // that is referenced or required by the submitted source code // is not a part of the contribution. // For example and not by way of limitation, // any systems/Windows libraries (WPF, WCF, ASP.NET etc.) // required to run the submitted source code is not a part of the contribution //====================================================================================================== using System; using System.Collections.Generic; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.Runtime.Serialization; using System.Configuration; using System.Diagnostics; using System.Reflection; using Microsoft.IdentityModel.Protocols.WSTrust; using Trade.Utility; using System.Collections.ObjectModel; using System.Threading; using Microsoft.IdentityModel.Claims; using System.IdentityModel.Tokens; namespace Trade.Client { public sealed class Connection { private T _channel; private string _clientConfig; private ChannelFactory _factory; private object _channelLock = new object(); string _userName; string _password; string _url; bool _useClaims; public Connection(T channel, string clientConfig, ChannelFactory factory, string url, string userName, string password, bool useClaims) { _channel = channel; _clientConfig = clientConfig; _factory = factory; _url = url; _userName = userName; _password = password; _useClaims = useClaims; } /// /// Returns a channel factory as an object type. The channel factory will be created as type "theType" /// /// The name of the binding configuration to be used when creating the factory. /// The endpoint address to be used when creating the channel factory. /// object representing a channel factory private static ChannelFactory createChannelFactory(string clientConfiguration, string userName, string password, bool useClaims) { return createChannelFactory(clientConfiguration, null, userName, password, useClaims); } /// /// Returns a channel factory as an object type. The channel factory will be created as type "theType" /// /// The type representing the service contract type. /// The name of the binding configuration to be used when creating the factory. /// The endpoint address to be used when creating the channel factory. /// object representing a channel factory private static ChannelFactory createChannelFactory(string clientConfiguration, string url, string userName, string password, bool useClaims) { ChannelFactory channelFactory = (ChannelFactory)Activator.CreateInstance(typeof(ChannelFactory), new object[] { clientConfiguration }); channelFactory.Credentials.UserName.UserName = userName; channelFactory.Credentials.UserName.Password = password; if (!string.IsNullOrEmpty(url)) { var oldAddress = channelFactory.Endpoint.Address; channelFactory.Endpoint.Address = new EndpointAddress(new Uri(url), oldAddress.Identity); } if (useClaims) { channelFactory.ConfigureChannelFactory(); } return channelFactory; } public string ClientConfig { get { return _clientConfig; } set { this._clientConfig = value; } } /// /// Returns a channel as an IChannel type. The channel will be created using a channel factory. /// /// The channel factory that will be used to create the channel instance. /// IChannel representing a WCF service channel public static T createChannel(ChannelFactory channelFactory) { if (channelFactory == null) return default(T); T returnChannel = default(T); try { returnChannel = channelFactory.CreateChannel(); } catch { throw; } return returnChannel; } /// /// Returns a channel as an IChannel type. The channel will be created using a channel factory. /// /// The channel factory that will be used to create the channel instance. /// IChannel representing a WCF service channel public static T createChannelActingAs(ChannelFactory channelFactory) { if (channelFactory == null) return default(T); T returnChannel = default(T); try { SecurityToken callerToken = null; IClaimsPrincipal claimsPrincipal = Thread.CurrentPrincipal as IClaimsPrincipal; if (claimsPrincipal != null) { foreach (IClaimsIdentity claimsIdentity in claimsPrincipal.Identities) { if (claimsIdentity.BootstrapToken is SamlSecurityToken) { callerToken = claimsIdentity.BootstrapToken; break; } } } returnChannel = channelFactory.CreateChannelActingAs(callerToken); } catch { throw; } return returnChannel; } public T Channel { get { T localChannel = _channel; if (localChannel == null) { lock (_channelLock) { if (_factory == null) { if (_url == null) _factory = (ChannelFactory)createChannelFactory(ClientConfig, _userName, _password, _useClaims); else _factory = (ChannelFactory)createChannelFactory(ClientConfig, _url, _userName, _password, _useClaims); } if (_useClaims) { return createChannelActingAs(_factory); } else if (_channel == null) { _channel = createChannel(_factory); } return _channel; } } return localChannel; } set { lock (_channelLock) { if (_channel != null && ((IChannel)_channel).State != CommunicationState.Opened) { ((IChannel)_channel).Abort(); _channel = default(T); } if (_factory != null && _factory.State != CommunicationState.Opened) { _factory.Abort(); _factory = null; } } } } public ChannelFactory Factory { get { return _factory; } set { this._factory = value; } } } /// /// This is the WCF master Client class. It will be instantiated once per remote operation (web service call) request, /// but uses a static Dictionary (connectionList) of type ClientList to ensure channels are chached and not instanced on a pre-request basis. /// The connecitonList will contain one entry per type of service connected to from an app/service client. The connection ClientList entry /// will in turn contain the list of all service nodes for this specific service type. /// public class Client { private static Dictionary> connectionDictionary = new Dictionary>(); private Connection _connection; /// /// /// /// /// public Client(string clientConfig) : this(clientConfig, null, null, null, false) { } public Client(string clientConfig, string url) : this(clientConfig, url, null, null, false) { } public Client(string clientConfig, string url, bool useClaims) : this(clientConfig, url, null, null, useClaims) { } /// /// /// /// /// public Client(string clientConfig, string userName, string password) : this(clientConfig, null, userName, password, false) { } public Client(string clientConfig, string url, string userName, string password) : this(clientConfig, url, userName, password, false) { } public Client(string clientConfig, string url, string userName, string password, bool useClaims) { if (!connectionDictionary.TryGetValue(typeof(T).FullName, out _connection)) { _connection = new Connection(default(T), clientConfig, null, url, userName, password, useClaims); connectionDictionary.Add(typeof(T).FullName, _connection); } } /// /// Property that is referenced in custom client code to get a channel from the connection list. Hence, when /// getting a channel via this property, load balancing and failover will occur. /// /// public T Channel { get { return _connection.Channel; } set { _connection.Channel = value; } } public static void ClearCache() { connectionDictionary = new Dictionary>(); } } }