//
// 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;
}
}
}