//*****************************************************************************
// Description.....Service Bus Tester
//
// Author..........Roman Kiss, rkiss@pathcom.com
// Copyright © 2011 ATZ Consulting Inc. (see included license.rtf file)
//
// Date Created: 11/11/11
//
// Date Modified By Description
//-----------------------------------------------------------------------------
// 11/11/11 Roman Kiss Initial Revision
//
//*****************************************************************************
//
#region Namespaces
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;
using System.Threading;
using Microsoft.ServiceBus;
using Microsoft.ServiceBus.Messaging;
#endregion
namespace RKiss.Tools.ServiceBusTester
{
#region Service activator
// this is a service activator acrross the appDomains
public sealed class ServiceHostActivator : MarshalByRefObject, IDisposable
{
DateTime _created = DateTime.Now;
string _name = string.Empty;
ServiceHostBase _host = null;
#region Dispose
void IDisposable.Dispose()
{
Close();
}
public override object InitializeLifetimeService()
{
// infinite lifetime
return null;
}
#endregion
#region Create
public static ServiceHostActivator Create(AppDomain appDomain, ConsumerConfigData config)
{
string _assemblyName = Assembly.GetAssembly(typeof(ServiceHostActivator)).FullName;
ServiceHostActivator activator = appDomain.CreateInstanceAndUnwrap(_assemblyName, typeof(ServiceHostActivator).ToString()) as ServiceHostActivator;
activator.SetHost(config);
return activator;
}
private void SetHost(ConsumerConfigData config)
{
try
{
if (_host == null)
{
//var binding = new NetMessagingBinding();
// version 1.2
var binding = new CustomBinding();
if (config.ContentType.StartsWith("application/soap+xml"))
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, Encoding.UTF8));
else if (config.ContentType.StartsWith("text/plain"))
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.None, Encoding.UTF8));
else if (config.ContentType.StartsWith("application/xml") || config.ContentType.StartsWith("application/json"))
binding.Elements.Add(new WebMessageEncodingBindingElement(System.Text.Encoding.UTF8));
binding.Elements.Add(new NetMessagingTransportBindingElement());
var securityBehavior = new TransportClientEndpointBehavior()
{
TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(config.IssuerName, config.IssuerSecret),
};
EndpointAddress ea = new EndpointAddress(config.TopicAddress);
var se = new ServiceEndpoint(ContractDescription.GetContract(typeof(IGenericOneWayContract)), binding, ea);
if(string.IsNullOrEmpty(config.SubscriptionAddress) == false)
se.ListenUri = new Uri(config.SubscriptionAddress);
if (config.RequiresSession)
se.Contract.SessionMode = SessionMode.Required;
se.Behaviors.Add(securityBehavior);
_host = new ServiceHost(typeof(VirtualService));
_host.AddServiceEndpoint(se);
_host.Extensions.Add(config);
_host.Faulted += new EventHandler(_host_Faulted);
_host.Closed += new EventHandler(_host_Closed);
_host.Opened += new EventHandler(_host_Opened);
_name = config.Name;
this.AddToStorage(this);
Trace.WriteLine(string.Format("ServiceHostActivator: host '{0}' has been created in the appDomain '{1}, contentType={2}'", config.Name, AppDomain.CurrentDomain.FriendlyName, config.ContentType));
}
else
{
throw new InvalidOperationException("The ServiceHost has been already setup");
}
}
finally
{
CallContext.FreeNamedDataSlot("_config");
}
}
void _host_Faulted(object sender, EventArgs e)
{
Trace.Write(string.Format("ServiceHostActivator.Faulted: {0}", (sender as ServiceHost).Description.Endpoints[0].Address));
}
void _host_Closed(object sender, EventArgs e)
{
Trace.Write(string.Format("ServiceHostActivator.Closed: {0}", (sender as ServiceHost).Description.Endpoints[0].Address));
}
void _host_Opened(object sender, EventArgs e)
{
Trace.Write(string.Format("ServiceHostActivator.Opened: {0}", (sender as ServiceHost).Description.Endpoints[0].Address));
}
private void AddToStorage(ServiceHostActivator activator)
{
List<ServiceHostActivator> activators = this.GetStorage();
if (activators.Exists(delegate(ServiceHostActivator host) { return host._host.Description.ConfigurationName == activator._host.Description.ConfigurationName; }))
{
throw new InvalidOperationException(string.Format("The service '{0}' is already hosted in the appDomain '{1}'", activator._host.Description.ConfigurationName, AppDomain.CurrentDomain.FriendlyName));
}
activators.Add(this);
}
private void RemoveFromStorage(ServiceHostActivator activator)
{
List<ServiceHostActivator> activators = this.GetStorage();
if (activators.Exists(delegate(ServiceHostActivator host) { return host._host.Description.ConfigurationName == activator._host.Description.ConfigurationName; }))
{
activators.Remove(activator);
}
}
private List<ServiceHostActivator> GetStorage()
{
string key = typeof(ServiceHostActivator).FullName;
List<ServiceHostActivator> activators = AppDomain.CurrentDomain.GetData(key) as List<ServiceHostActivator>;
if (activators == null)
{
lock (AppDomain.CurrentDomain.FriendlyName)
{
activators = AppDomain.CurrentDomain.GetData(key) as List<ServiceHostActivator>;
if (activators == null)
{
activators = new List<ServiceHostActivator>();
AppDomain.CurrentDomain.SetData(key, activators);
}
}
}
return activators;
}
#endregion
#region Remoting
public void Open()
{
if (_host != null)
{
try
{
if (_host.State == CommunicationState.Created)
{
_host.Open();
Trace.WriteLine(string.Format("ServiceHostActivator '{0}' opened", this.Name));
}
}
catch (Exception ex)
{
RemoveFromStorage(this);
Trace.WriteLine(string.Format("Opening ServiceHostActivator '{0}' failed: '{1}'", this.Name, ex.Message));
throw ex;
}
}
}
public void Close()
{
if (_host != null)
{
try
{
if (_host.State == CommunicationState.Opened || _host.State == CommunicationState.Created)
{
_host.Close();
Trace.WriteLine(string.Format("ServiceHostActivator '{0}' closed", this.Name));
}
else if (_host.State == CommunicationState.Faulted)
{
Trace.WriteLine(string.Format("ServiceHostActivator '{0}' faulted", this.Name));
_host.Abort();
}
}
catch (Exception ex)
{
Trace.WriteLine(string.Format("Closing ServiceHostActivator '{0}' failed: '{1}'", this.Name, ex.Message));
throw ex;
}
}
}
public void Abort()
{
if (_host != null)
{
try
{
_host.Abort();
Trace.WriteLine(string.Format("ServiceHostActivator '{0}' aborted", this.Name));
}
catch (Exception ex)
{
Trace.WriteLine(string.Format("Aborting ServiceHostActivator '{0}' failed: '{1}'", this.Name, ex.Message));
throw ex;
}
}
}
public ServiceHostBase Host { get { return _host; } } // valid only for default domain!
public AppDomain AppDomainHost { get { return AppDomain.CurrentDomain; } }
public string Name { get { return _name; } }
public DateTime Created { get { return _created; } }
public CommunicationState State { get { return _host.State; } }
#endregion
}
#endregion
#region HostServices
public sealed class HostServices : IDisposable
{
#region Private Members
ReaderWriterLock _rwl = new ReaderWriterLock();
string _hostName = string.Empty;
List<AppDomain> _appDomains = new List<AppDomain>();
static string _key = typeof(ServiceHostActivator).FullName;
bool _selfHosted = false;
#endregion
#region Constructors
public HostServices()
: this(false)
{
}
public HostServices(bool selfHosted)
{
_selfHosted = selfHosted;
Trace.WriteLine(string.Format("The HostServices '{0}' runtime has been created, selfHosted={1}", _hostName, selfHosted));
}
#endregion
#region IDisposable Members
public void Dispose()
{
try
{
_rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));
_appDomains.Clear();
}
finally
{
_rwl.ReleaseWriterLock();
}
}
#endregion
#region HostName
public string HostName
{
get { return _hostName; }
}
#endregion
#region Add
public void Add(string appDomainName, ConsumerConfigData config)
{
try
{
_rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));
appDomainName = ValidateAppDomainName(appDomainName);
AppDomain appDomain = this.CreateDomainHost(appDomainName);
ServiceHostActivator.Create(appDomain, config);
}
finally
{
_rwl.ReleaseWriterLock();
}
}
private AppDomain CreateDomainHost(string appDomainName)
{
AppDomain appDomain = _appDomains.Find(delegate(AppDomain ad) { return ad.FriendlyName == appDomainName; });
if (appDomain == null)
{
appDomain = AppDomain.CurrentDomain.FriendlyName == appDomainName ? AppDomain.CurrentDomain : AppDomain.CreateDomain(appDomainName);
_appDomains.Add(appDomain);
Trace.WriteLine(string.Format("The AppDomain '{0}' has been created", appDomainName));
appDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs e)
{
Trace.WriteLine(string.Format("[{0}] UnhandledException = {1}", (sender as AppDomain).FriendlyName, e.ExceptionObject));
};
appDomain.DomainUnload += delegate(object sender, EventArgs e)
{
Trace.WriteLine(string.Format("[{0}] DomainUnload", (sender as AppDomain).FriendlyName));
};
appDomain.ProcessExit += delegate(object sender, EventArgs e)
{
Trace.WriteLine(string.Format("[{0}] ProcessExit", (sender as AppDomain).FriendlyName ));
};
}
return appDomain;
}
private string ValidateAppDomainName(string appDomainName)
{
if (string.IsNullOrEmpty(appDomainName) || appDomainName == "*" || appDomainName.ToLower() == "default")
{
appDomainName = AppDomain.CurrentDomain.FriendlyName;
}
return appDomainName;
}
#endregion
public bool IsDomainExists(string appDomainName)
{
try
{
_rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));
appDomainName = ValidateAppDomainName(appDomainName);
AppDomain appDomain = _appDomains.Find(delegate(AppDomain ad) { return ad.FriendlyName == appDomainName; });
return appDomain != null;
}
finally
{
_rwl.ReleaseWriterLock();
}
}
#region GetAppDomainHost
public AppDomain this[string appDomainName]
{
get
{
try
{
_rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));
appDomainName = ValidateAppDomainName(appDomainName);
AppDomain appDomain = _appDomains.Find(delegate(AppDomain ad) { return ad.FriendlyName == appDomainName; });
if (appDomain != null)
{
return appDomain;
}
else
{
throw new InvalidOperationException(string.Format("Requested appDomain '{0}' doesn't exist in the catalog", appDomainName));
}
}
finally
{
_rwl.ReleaseWriterLock();
}
}
}
#endregion
#region GetHostedServices
public List<ServiceHostActivatorStatus> GetHostedServices
{
get
{
List<ServiceHostActivatorStatus> lists = new List<ServiceHostActivatorStatus>();
try
{
_rwl.AcquireReaderLock(TimeSpan.FromSeconds(60));
foreach (AppDomain appDomain in _appDomains)
{
List<ServiceHostActivator> activators = appDomain.GetData(_key) as List<ServiceHostActivator>;
if (activators != null)
{
foreach (ServiceHostActivator activator in activators)
{
lists.Add(new ServiceHostActivatorStatus
{
Created=activator.Created,
AppDomainHostName = activator.AppDomainHost.FriendlyName,
State=activator.State,
Name=activator.Name,
//SubscriptionAddress = activator.
});
}
}
}
}
finally
{
_rwl.ReleaseReaderLock();
}
return lists;
}
}
#endregion
#region GetHostedService
public ServiceHostBase GetHostedService(string name, bool flagRemove)
{
ServiceHostBase service = null;
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Not valid service name");
try
{
_rwl.AcquireReaderLock(TimeSpan.FromSeconds(60));
foreach (AppDomain appDomain in _appDomains)
{
List<ServiceHostActivator> activators = appDomain.GetData(_key) as List<ServiceHostActivator>;
if (activators != null)
{
ServiceHostActivator activator = activators.Find(delegate(ServiceHostActivator a) { return a.Name == name; });
if (activator != null)
{
service = activator.Host;
if (flagRemove)
{
activators.Remove(activator);
}
}
}
}
}
finally
{
_rwl.ReleaseReaderLock();
}
return service;
}
#endregion
#region Open
public int Open()
{
int count = 0;
try
{
_rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));
foreach (AppDomain ad in _appDomains)
{
List<ServiceHostActivator> activators = ad.GetData(_key) as List<ServiceHostActivator>;
if (activators != null)
{
activators.ForEach(delegate(ServiceHostActivator activator)
{
activator.Open();
});
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
count = _appDomains.Count;
_rwl.ReleaseWriterLock();
}
return count;
}
public void Open(string appDomainName)
{
try
{
_rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));
appDomainName = ValidateAppDomainName(appDomainName);
AppDomain appDomain = _appDomains.Find(delegate(AppDomain ad) { return ad.FriendlyName == appDomainName; });
if (appDomain != null)
{
if (appDomain.IsDefaultAppDomain() == false)
{
List<ServiceHostActivator> activators = appDomain.GetData(_key) as List<ServiceHostActivator>;
if (activators != null)
{
activators.ForEach(delegate(ServiceHostActivator activator)
{
Trace.WriteLine(string.Format("Open service '{0}'", activator.Name));
activator.Open();
});
}
Trace.WriteLine(string.Format("Open services in the AppDomain '{0}'", appDomainName));
}
}
else
{
throw new InvalidOperationException(string.Format("Open '{0}' appDomain host failed - doesn't exist", appDomainName));
}
}
finally
{
_rwl.ReleaseWriterLock();
}
}
#endregion
#region Close
public void Close()
{
try
{
_rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));
_appDomains.Reverse();
foreach (AppDomain ad in _appDomains)
{
List<ServiceHostActivator> activators = ad.GetData(_key) as List<ServiceHostActivator>;
if (activators != null)
{
activators.Reverse();
activators.ForEach(delegate(ServiceHostActivator activator)
{
activator.Close();
});
activators.Clear();
}
}
int count = _appDomains.RemoveAll(delegate(AppDomain ad)
{
if (!ad.IsDefaultAppDomain())
{
AppDomain.Unload(ad);
}
return true;
});
_appDomains.Clear();
}
finally
{
_rwl.ReleaseWriterLock();
}
}
public void Close(string appDomainName)
{
this.Close(appDomainName, true);
}
public bool Close(string appDomainName, bool bThrow)
{
bool bHasBeenClosed = false;
try
{
_rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));
appDomainName = ValidateAppDomainName(appDomainName);
AppDomain appDomain = _appDomains.Find(delegate(AppDomain ad) { return ad.FriendlyName == appDomainName; });
if (appDomain != null)
{
if (appDomain.IsDefaultAppDomain() == false)
{
List<ServiceHostActivator> activators = appDomain.GetData(_key) as List<ServiceHostActivator>;
activators.Reverse();
if (activators != null)
{
activators.ForEach(delegate(ServiceHostActivator activator)
{
activator.Close();
});
}
// clean-up
activators.Clear();
_appDomains.Remove(appDomain);
AppDomain.Unload(appDomain);
bHasBeenClosed = true;
}
else if (bThrow)
{
throw new InvalidOperationException("The Close operation can't be processed on the default appDomain");
}
}
}
finally
{
_rwl.ReleaseWriterLock();
}
return bHasBeenClosed;
}
#endregion
#region Abort
public bool Abort(string appDomainName, bool bThrow)
{
bool bHasBeenAborted = false;
try
{
_rwl.AcquireWriterLock(TimeSpan.FromSeconds(60));
appDomainName = ValidateAppDomainName(appDomainName);
AppDomain appDomain = _appDomains.Find(delegate(AppDomain ad) { return ad.FriendlyName == appDomainName; });
if (appDomain != null)
{
if (appDomain.IsDefaultAppDomain() == false)
{
List<ServiceHostActivator> activators = appDomain.GetData(_key) as List<ServiceHostActivator>;
activators.Reverse();
if (activators != null)
{
activators.ForEach(delegate(ServiceHostActivator activator)
{
activator.Abort();
});
}
// clean-up
activators.Clear();
_appDomains.Remove(appDomain);
AppDomain.Unload(appDomain);
bHasBeenAborted = true;
}
else if (bThrow)
{
throw new InvalidOperationException("The Abort operation can't be processed on the default appDomain");
}
}
}
finally
{
_rwl.ReleaseWriterLock();
}
return bHasBeenAborted;
}
#endregion
#region Current
public static HostServices Current
{
get
{
string key = typeof(HostServices).FullName;
HostServices hostservices = AppDomain.CurrentDomain.GetData(key) as HostServices;
if (hostservices == null)
{
lock (AppDomain.CurrentDomain.FriendlyName)
{
hostservices = AppDomain.CurrentDomain.GetData(key) as HostServices;
if (hostservices == null)
{
hostservices = new HostServices(true);
AppDomain.CurrentDomain.SetData(key, hostservices);
}
}
}
return hostservices;
}
}
#endregion
}
#endregion
}