// // 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 Trade.Utility; using Trade.BusinessServiceContract; using Trade.OrderProcessorContract; namespace Trade.Client { public sealed class ClientList { public Connection _connection; private Type _type; private string _clientConfig; private string _userName; private string _password; /// /// Constructor logic. /// /// Friendly name of the virtual endpoint this ClientList instance will initialize. public ClientList(Type clientContractType, string clientConfig) { _clientConfig = clientConfig; _type = clientContractType; _connection = new Connection(_type, null, _clientConfig, null); } /// /// /// /// /// public ClientList(Type clientContractType, string clientConfig, string userName, string password) { _clientConfig = clientConfig; _type = clientContractType; _userName = userName; _password = password; _connection = new Connection(_type, null, _clientConfig, null,userName,password); } public class Connection { private IChannel _channel; private string _clientConfig; private ChannelFactory _factory; private object _channelLock = new object(); public Type _type; string _userName; string _password; /// /// Creates a new Connection instance, which stores all the information for a cached Channel instance /// /// /// /// /// /// public Connection(Type type, IChannel channel, string clientConfig, ChannelFactory factory) { _type = type; _channel = channel; _clientConfig = clientConfig; _factory = factory; } public Connection(Type type, IChannel channel, string clientConfig, ChannelFactory factory, string userName, string password) { _type = type; _channel = channel; _clientConfig = clientConfig; _factory = factory; _userName = userName; _password = password; } /// /// Uses reflection to create a typed generic object. /// /// /// /// /// /// public static object CreateGeneric(Type generic, Type innerType, params object[] args) { try { System.Type specificType = generic.MakeGenericType(new System.Type[] { innerType }); return Activator.CreateInstance(specificType, args); } catch { throw; } } /// /// 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 public static object createChannelFactory(Type theType, string clientConfiguration, string userName, string password) { object channelFactory = CreateGeneric(typeof(ChannelFactory<>), theType, new object[] { clientConfiguration}); ((ChannelFactory)channelFactory).Credentials.UserName.UserName = userName; ((ChannelFactory)channelFactory).Credentials.UserName.Password = password; 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 IChannel createChannel(object channelFactory) { if (channelFactory == null) return null; IChannel returnChannel = null; try { returnChannel = (IChannel)channelFactory.GetType().InvokeMember("CreateChannel", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, channelFactory, null); } catch { throw; } return returnChannel; } public IChannel Channel { get { IChannel localChannel = _channel; if (localChannel == null) { lock (_channelLock) { if (_factory == null) { _factory = (ChannelFactory)createChannelFactory(_type, ClientConfig,_userName,_password); } 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 = null; } 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; public ClientList connection; private string _clientConfig; private Type _type; /// /// /// /// /// public Client(Type clientContractType, string clientConfig) { _type = clientContractType; _clientConfig = clientConfig; if (connectionDictionary == null) connectionDictionary = new Dictionary(); try { connection = connectionDictionary[_type.FullName]; } catch { //No entry! connection = new ClientList(clientContractType,clientConfig); connectionDictionary.Add(_type.FullName, connection); } } /// /// /// /// /// public Client(Type clientContractType, string clientConfig, string userName, string password) { _type = clientContractType; _clientConfig = clientConfig; if (connectionDictionary == null) connectionDictionary = new Dictionary(); try { connection = connectionDictionary[_type.FullName]; } catch { //No entry! connection = new ClientList(clientContractType, clientConfig, userName, password); connectionDictionary.Add(_type.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 IChannel Channel { get { return connection._connection.Channel; } set { connection._connection.Channel = value; } } public static void ClearCache() { connectionDictionary = null; } } }