/// <summary> /// Opens the authenticator using the settings passed. /// </summary> /// <param name="router">The router to be used for messaging.</param> /// <param name="settings">The <see cref="AuthenticatorSettings" /> instance to use.</param> public void Open(MsgRouter router, AuthenticatorSettings settings) { using (TimedLock.Lock(this)) { if (isOpen) { throw new AuthenticationException("Authenticator is already open."); } // Load the configuration settings cacheFlushInterval = settings.CacheFlushInterval; successTTL = settings.SuccessTTL; failTTL = settings.FailTTL; // Crank this sucker up. if (settings.MaxCacheSize > 0) { cache = new TimedLRUCache <string, AuthenticationResult>(); cache.MaxItems = settings.MaxCacheSize; } else { cache = null; } this.router = router; this.bkTimer = new GatedTimer(new TimerCallback(OnBkTimer), null, settings.BkTaskInterval); this.isOpen = true; this.cacheFlushTime = SysTime.Now + cacheFlushInterval; router.Dispatcher.AddTarget(this); } }
/// <summary> /// Constructor. /// </summary> /// <param name="router">The associated router.</param> /// <param name="routeTTL">The lifetime of an unrenewed route.</param> public PhysicalRouteTable(MsgRouter router, TimeSpan routeTTL) { this.router = router; this.routerEP = router.RouterEP; this.routeTTL = routeTTL; this.routes = new Dictionary <string, PhysicalRoute>(); }
public void ConfigServiceHandler_Error_NoSettings() { MsgRouter router = null; Config config; try { InitFiles(); router = CreateRouter(); config = Query(router, "machine", "foo.exe", new Version("3.0"), "other"); Assert.Fail("Expected a no settings available error"); } catch (SessionException) { } finally { if (router != null) { router.Stop(); router = null; } ClearFiles(); } }
private int cbRecv; // Bytes to receive /// <summary> /// Constructor. /// </summary> /// <param name="router">The associated message router.</param> public TcpChannel(MsgRouter router) { this.router = router; this.sock = null; this.routerEP = null; this.remoteEP = null; this.localEP = null; this.connected = false; this.initProcessed = false; this.lastAccess = SysTime.Now; this.isUplink = false; this.isDownlink = false; this.isP2P = false; this.sending = false; this.sendMsg = null; this.sendQueue = new PriorityQueue <Msg>(); this.onSend = new AsyncCallback(OnSend); this.sendBuf = null; this.sendPos = 0; this.cbSend = 0; this.onReceive = new AsyncCallback(OnReceive); this.recvHeader = false; this.recvBuf = null; this.recvPos = 0; }
/// <summary> /// Releases all resources associated with the instance. /// </summary> public void Close() { using (TimedLock.Lock(syncLock)) { if (syncLock == null) { return; } router.Dispatcher.RemoveTarget(this); if (packageFolder != null) { packageFolder.Dispose(); packageFolder = null; } if (bkTimer != null) { bkTimer.Dispose(); bkTimer = null; } router = null; syncLock = null; } }
public void Error_MaxIncludeDepth() { MsgRouter router = null; Config config; try { InitFiles(); router = CreateRouter(); WriteFile("svr-machine.ini", "##include test1.ini"); for (int i = 0; i < 16; i++) { WriteFile(string.Format("test{0}.ini", i), string.Format("&&include test{0}.ini", i + 1)); } config = Query(router, "machine", "foo.exe", new Version("3.0"), "other"); Assert.Fail("Expected a max include depth error"); } catch (SessionException) { } finally { if (router != null) { router.Stop(); router = null; } ClearFiles(); } }
/// <summary> /// Immediately terminates the processing of all client messages. /// </summary> public void Stop() { if (router == null) { return; } using (TimedLock.Lock(syncLock)) { if (packageFolder != null) { packageFolder.Dispose(); packageFolder = null; } if (bkTimer != null) { bkTimer.Dispose(); bkTimer = null; } if (cluster != null) { cluster.Stop(); cluster = null; } if (router != null) { router.Dispatcher.RemoveTarget(this); router = null; } } }
private DateTime nextPurgeTime; // Next scheduled package folder purge time (SYS) /// <summary> /// Constructor. /// </summary> public AppStoreClient() { this.syncLock = null; this.router = null; this.packageFolder = null; this.bkTimer = null; }
/// <summary> /// Initializes a server side session. /// </summary> /// <param name="router">The associated message router.</param> /// <param name="sessionMgr">The associated session manager.</param> /// <param name="ttl">Session time-to-live.</param> /// <param name="msg">The message that triggered this session.</param> /// <param name="target">The dispatch target instance.</param> /// <param name="method">The dispatch method.</param> /// <param name="sessionInfo"> /// The session information associated with the handler or <c>null</c> /// to use session defaults. /// </param> public virtual void InitServer(MsgRouter router, ISessionManager sessionMgr, TimeSpan ttl, Msg msg, object target, MethodInfo method, SessionHandlerInfo sessionInfo) { if (sessionInfo == null) { sessionInfo = SessionHandlerInfo.Default; } this.isClient = false; this.router = router; this.sessionMgr = sessionMgr; this.sessionID = msg._SessionID; this.isAsync = sessionInfo.IsAsync; this.isRunning = true; this.startTime = SysTime.Now; this.finishTime = DateTime.MaxValue; this.ttd = startTime + ttl; this.ttl = ttl; this.serverInitMsg = msg; this.clientEP = msg._FromEP; this.target = target; this.method = method; this.sessionInfo = sessionInfo != null ? sessionInfo : defSessionInfo; this.cachedReply = null; msg._Session = (ISession)this; }
//--------------------------------------- // Clustering Implementation Note: // // Note that the one of the application store instances in the cluster needs // to be configured as the primary application store. This is done by setting // the following configuration setting: // // LillTek.Datacenter.AppStore = Primary // // The primary application store holds the definitive set of application // packages against which the non-primary store instances will synchronize // themselves. Note that the primary application store is not necessarily // the cluster master. These two concepts are independent. The cluster master // will be elected normally from the pool of application store instances and // will be responsible for replicating cluster state global and instance state. // // At this point there is no global cluster state. Each instance does expose // the following properties to the cluster: // // Mode This is set to "Primary" or "Cache" and indicates which // application store instances are configured to be the // primary instance (For this implementation, only one // instance should be configured as the primary). /// <summary> /// Constructs a application store service handler instance. /// </summary> public AppStoreHandler() { this.router = null; this.syncLock = null; this.bkTimer = null; this.cluster = null; }
/// <summary> /// Immediately terminates the processing of all client messages. /// </summary> public void Stop() { if (!isRunning) { return; } using (TimedLock.Lock(syncLock)) { if (bkTimer != null) { bkTimer.Dispose(); bkTimer = null; } if (httpServer != null) { httpServer.Stop(); httpServer = null; } router = null; isRunning = false; } }
/// <summary> /// Private constructor for initializing <b>server side</b> sessions. /// </summary> /// <param name="router">The message router.</param> /// <param name="msg">The <see cref="ReliableTransferMsg" /> that initiated this session.</param> /// <param name="direction">The transfer direction supported by the server.</param> /// <param name="stream">The input or output stream.</param> /// <exception cref="InvalidOperationException"> /// Thrown if the client requested transfer direction does not match the /// direction implied by this method. /// </exception> private StreamTransferSession(MsgRouter router, ReliableTransferMsg msg, TransferDirection direction, EnhancedStream stream) { if (direction != msg.Direction) { throw new InvalidOperationException(string.Format("Transfer direction [{0}] is not supported.", msg.Direction)); } ReliableTransferHandler handler; reliableSession = msg.Session; reliableSession.SessionHandler = handler = new ReliableTransferHandler(reliableSession); handler.BeginTransferEvent += new ReliableTransferDelegate(OnBeginTransfer); handler.EndTransferEvent += new ReliableTransferDelegate(OnEndTransfer); if (direction == TransferDirection.Upload) { handler.ReceiveEvent += new ReliableTransferDelegate(OnReceive); } else { handler.SendEvent += new ReliableTransferDelegate(OnSend); } this.router = router; this.direction = direction; this.args = msg.Args; this.stream = stream; this.started = false; this.closed = false; this.arTransfer = null; this.simError = false; this.simCancel = false; this.delay = 0; }
/// <summary> /// Private constructor for initializing <b>client side</b> sessions. /// </summary> /// <param name="router">The message router.</param> /// <param name="serverEP">The server endpoint.</param> /// <param name="direction">The transfer direction.</param> /// <param name="stream">The input or output stream.</param> private StreamTransferSession(MsgRouter router, MsgEP serverEP, TransferDirection direction, EnhancedStream stream) { ReliableTransferHandler handler; reliableSession = router.CreateReliableTransferSession(); reliableSession.SessionHandler = handler = new ReliableTransferHandler(reliableSession); handler.BeginTransferEvent += new ReliableTransferDelegate(OnBeginTransfer); handler.EndTransferEvent += new ReliableTransferDelegate(OnEndTransfer); if (direction == TransferDirection.Upload) { handler.SendEvent += new ReliableTransferDelegate(OnSend); } else { handler.ReceiveEvent += new ReliableTransferDelegate(OnReceive); } this.router = router; this.serverEP = serverEP; this.direction = direction; this.args = null; this.stream = stream; this.started = false; this.closed = false; this.streamClosed = false; this.arTransfer = null; this.simError = false; this.simCancel = false; this.delay = 0; }
private static int refCount = 0; // Start() reference count /// <summary> /// Used by services and applications to start the <see cref="ChannelHost" />, /// using the result of <see cref="Assembly" />.<see cref="Assembly.GetEntryAssembly" /> /// to determine the location of the application configuration file. /// </summary> /// <remarks> /// <para> /// Calls to this method may be nested. A reference count is maintained and only /// the first call will actually perform any initialization. /// </para> /// <note> /// All successful calls to <see cref="Start()" /> must eventually be matched with /// a call to <see cref="Stop" /> global state including the underlying LillTek /// <see cref="MsgRouter" /> will be released properly. /// </note> /// </remarks> public static void Start() { using (TimedLock.Lock(SyncRoot)) { if (refCount == 0) { if (!Helper.IsInitialized) { Assembly entryAssembly = Assembly.GetEntryAssembly(); if (entryAssembly == null) { SysLog.LogWarning("Unable obtain the entry assembly via Assembly.GetEntryAssembly(). " + "The application's LillTek configuration settings will not be available. " + "You may call LillTek.ServiceModel.ChannelHost.Start(Assembly) with a valid " + "assembly during application initialization to correct this."); entryAssembly = Assembly.GetExecutingAssembly(); // Use the current assembly instead } Helper.InitializeApp(entryAssembly); } RegisterMsgTypes(); MsgRouter.StartGlobal(); } refCount++; } }
public void ConfigServiceHandler_Error_MultipleDefault() { MsgRouter router = null; Config config; try { InitFiles(); router = CreateRouter(); try { WriteFile("svr-machine.ini", "##switch $(usage)\r\n##default\r\n##default\r\n##endswitch"); config = Query(router, "machine", "foo.exe", new Version("3.0"), "other"); Assert.Fail("Expected a multiple ##default error"); } catch (SessionException) { } } finally { if (router != null) { router.Stop(); router = null; } ClearFiles(); } }
/// <summary> /// Opens the topology instance in server mode. /// </summary> /// <param name="router">The message router to be used by the cluster.</param> /// <param name="dynamicScope">The dynamic scope name.</param> /// <param name="target">The target object whose dynamic message handlers are to be registered (or <c>null</c>).</param> /// <param name="clusterEP">The cluster's logical endpoint.</param> /// <param name="args">Topology implementation specific parameters (ignored for this implementation).</param> /// <remarks> /// <para> /// This method also registers the dynamic message handlers that case-insensitvely /// match the dynamicScope parameter passed that are found within the /// target object passed with the router's message dispatcher, after performing /// any necessary munging of their message endpoints. This is done by /// matching the dynamicScope parameter passed against the <see cref="MsgHandler.DynamicScope" /> /// property in the <see cref="MsgHandler" /> attribute tagging the message handler. /// </para> /// <para> /// The matching message handler endpoints will be set to clusterEP. /// </para> /// </remarks> public virtual void OpenServer(MsgRouter router, string dynamicScope, MsgEP clusterEP, object target, ArgCollection args) { if (!clusterEP.IsLogical) { throw new ArgumentException(TopologyHelper.ClusterEPNotLogicalMsg); } if (!router.EnableP2P) { SysLog.LogWarning(NoP2PMsg); } this.router = router; this.instanceID = Helper.NewGuid(); this.clusterEP = clusterEP; this.broadcastEP = new MsgEP(clusterEP, "*"); this.instanceEP = new MsgEP(clusterEP, Helper.NewGuid().ToString()); this.isClient = false; if (target != null) { router.Dispatcher.AddTarget(target, dynamicScope, this, null); } OnLogicalChange(); // Forces the initial load of the instance EPs }
public void ConfigServiceHandler_Include() { MsgRouter router = null; Config config; try { InitFiles(); router = CreateRouter(); WriteFile("app-foo.exe.ini", @" test.foo=foo &&include include.ini "); WriteFile("include.ini", @" test.bar=foobar "); config = Query(router, "machine", "foo.exe", new Version("1.0"), ""); Assert.AreEqual("foo", config.Get("test.foo")); Assert.AreEqual("foobar", config.Get("test.bar")); } finally { if (router != null) { router.Stop(); router = null; } ClearFiles(); } }
/// <summary> /// Opens the <see cref="AppStoreClient" /> instance so that it is ready to /// process requests. /// </summary> /// <param name="router">The <see cref="MsgRouter" /> to be associated with the client.</param> /// <param name="settings">The <see cref="AppStoreClientSettings" /> to be used.</param> /// <exception cref="InvalidOperationException">Thrown if the instance is already open.</exception> public void Open(MsgRouter router, AppStoreClientSettings settings) { if (this.syncLock != null) { throw new InvalidOperationException("AppStoreClient is already open."); } // Make sure that the LillTek.Datacenter message types have been // registered with the LillTek.Messaging subsystem. LillTek.Datacenter.Global.RegisterMsgTypes(); // Initialize using (TimedLock.Lock(router.SyncRoot)) { this.syncLock = router.SyncRoot; this.router = router; this.settings = settings; this.bkTimer = new GatedTimer(new System.Threading.TimerCallback(OnBkTimer), null, settings.BkTaskInterval); this.nextPurgeTime = SysTime.Now; if (settings.LocalCache) { this.packageFolder = new AppPackageFolder(syncLock, settings.PackageFolder); } else { this.packageFolder = null; } router.Dispatcher.AddTarget(this); } }
/// <summary> /// Opens the topology instance in client mode. /// </summary> /// <param name="router">The message router to be used by the cluster.</param> /// <param name="clusterEP">The cluster's logical endpoint.</param> /// <param name="args">Topology implementation specific parameters (ignored for this implementation).</param> /// <remarks> /// </remarks> public virtual void OpenClient(MsgRouter router, MsgEP clusterEP, ArgCollection args) { if (!clusterEP.IsLogical) { throw new ArgumentException(TopologyHelper.ClusterEPNotLogicalMsg); } if (!router.EnableP2P) { SysLog.LogWarning(NoP2PMsg); } this.router = router; this.instanceID = Helper.NewGuid(); this.clusterEP = clusterEP; this.broadcastEP = new MsgEP(clusterEP, "*"); this.instanceEP = null; this.isClient = true; ArgCollection argsCopy; argsCopy = args.Clone(); argsCopy["topology-type"] = TopologyHelper.SerializeType(this.GetType()); serialized = argsCopy.ToString(); OnLogicalChange(); // Forces the initial load of the instance EPs }
public void _Trace(MsgRouter router, Exception e) { const string format = @"Exception: {0} Message: {1} Stack: "; StringBuilder sb; string summary; string details; sb = new StringBuilder(120); _TraceSummary(router, sb); summary = sb.ToString(); sb = new StringBuilder(512); sb.AppendFormat(null, format, e.GetType().ToString(), e.Message); sb.AppendFormat(e.StackTrace); sb.Append("----------\r\n"); _TraceDetails(router, sb); details = sb.ToString(); NetTrace.Write(MsgRouter.TraceSubsystem, 0, "Exception: [" + this.GetType().Name + "]", summary, details); }
/// <summary> /// Opens the topology instance in server mode. /// </summary> /// <param name="router">The message router to be used by the cluster.</param> /// <param name="dynamicScope">The dynamic scope name.</param> /// <param name="target">The target object whose dynamic message handlers are to be registered (or <c>null</c>).</param> /// <param name="clusterEP">The cluster's logical endpoint.</param> /// <param name="args">Topology implementation specific parameters.</param> /// <remarks> /// <para> /// This method also registers the dynamic message handlers that case-insensitvely /// match the dynamicScope parameter passed that are found within the /// target object passed with the router's message dispatcher, after performing /// any necessary munging of their message endpoints. This is done by /// matching the dynamicScope parameter passed against the <see cref="MsgHandler.DynamicScope" /> /// property in the <see cref="MsgHandler" /> attribute tagging the message handler. /// </para> /// <para> /// The matching message handler endpoints will be set to clusterEP. /// </para> /// </remarks> public virtual void OpenServer(MsgRouter router, string dynamicScope, MsgEP clusterEP, object target, ArgCollection args) { string arg; int index; if (!clusterEP.IsLogical) { throw new ArgumentException(TopologyHelper.ClusterEPNotLogicalMsg); } this.router = router; this.instanceID = Helper.NewGuid(); this.clusterEP = clusterEP; this.broadcastEP = new MsgEP(clusterEP, "*"); this.isClient = false; GetServiceInstances(clusterEP, args); arg = args["this-instance"]; if (arg == null || !int.TryParse(arg, out index) || index < 0 || index >= instances.Length) { throw new ArgumentException("[this-instance] topology argument is not a valid index into the instances array."); } instanceEP = instances[index]; if (target != null) { router.Dispatcher.AddTarget(target, dynamicScope, this, null); } }
/// <summary> /// Instantiates and initializes an <see cref="IReliableMessenger" /> plug-in defined by a configuration setting /// and then opens it as a server side messenger. /// </summary> /// <param name="router">The message router to associate with the messenger.</param> /// <param name="argsKey">The fully qualified configuration key specifying the messenger arguments formatted for <see cref="ArgCollection" />.</param> /// <returns>The messenger instance.</returns> /// <remarks> /// <para> /// The messenger arguments must include an argument named <b>messenger-type</b> which must be /// a reference to a <see cref="IReliableMessenger" /> implementation formatted as described /// in <see cref="Config.Parse(string,System.Type)" />. /// </para> /// <para> /// This utility method maps the messenger type reference to a .NET assembly and type and then /// instantiates an instance and then opens it as a client side messenger. The arguments loaded /// are then passed to the messenger's <see cref="IReliableMessenger.OpenServer" /> method. /// </para> /// </remarks> public static IReliableMessenger OpenServer(MsgRouter router, string argsKey) { IReliableMessenger messenger; ArgCollection args; string messengerType; System.Type type; if (!router.IsOpen) { throw new InvalidOperationException(RouterClosedMsg); } args = ArgCollection.Parse(Config.Global.Get(argsKey)); messengerType = args["messenger-type"]; if (messengerType == null) { throw new ArgumentException("Messenger arguments must specify [messenger-type]."); } type = Config.Parse(messengerType, (System.Type)null); if (type == null || !typeof(IReliableMessenger).IsAssignableFrom(type)) { throw new ArgumentException(string.Format("Unable to map setting [{0}] into an IReliableMessenger.", messengerType)); } messenger = Helper.CreateInstance <IReliableMessenger>(type); messenger.OpenServer(router, args); return(messenger); }
public void _Trace(MsgRouter router, int detail, string tEvent, string summaryAppend, params string[] args) { StringBuilder sb; string summary; string details; sb = new StringBuilder(120); _TraceSummary(router, sb); summary = sb.ToString(); if (summaryAppend != null) { summary += summaryAppend; } sb = new StringBuilder(512); _TraceDetails(router, sb); details = sb.ToString(); for (int i = 0; i < args.Length; i++) { sb.Append(args[i] + "\r\n"); } NetTrace.Write(MsgRouter.TraceSubsystem, detail, tEvent + ": [" + this.GetType().Name + "]", summary, details); }
private static StaticHashedTopology OpenServer(MsgRouter router, string dynamicScope, object target, string clusterEP, int instance) { System.Type type = typeof(StaticHashedTopology); Config.SetConfig(string.Format(ClusterConfig, type.FullName, type.Assembly.Location, clusterEP, instance)); return((StaticHashedTopology)TopologyHelper.OpenServer(router, dynamicScope, target, "clusterType", "clusterArgs")); }
private static StaticHashedTopology OpenClient(MsgRouter router, string clusterEP) { System.Type type = typeof(StaticHashedTopology); Config.SetConfig(string.Format(ClusterConfig, type.FullName, type.Assembly.Location, clusterEP, "")); return((StaticHashedTopology)TopologyHelper.OpenClient(router, "clusterType", "clusterArgs")); }
public void MsgDispatcherLogical_DynamicEP() { MsgDispatcher dispatcher; MsgRouter router; ChannelEP target; dispatcher = new MsgDispatcher(); router = new MsgRouter(dispatcher); router.Dispatcher.AddTarget(this, "foo", new Munger(), null); router.RouterEP = MsgEP.Parse("physical://foo.com/" + Helper.NewGuid().ToString()); router.Start(IPAddress.Any, null, new IPEndPoint(IPAddress.Any, 0), new IPEndPoint(IPAddress.Any, 0), 5, TimeSpan.FromSeconds(60)); target = new ChannelEP(Transport.Udp, router.UdpEP); try { Clear(); router.Transmit(target, new _DispatchMsg1("logical://MungedDynamic")); Thread.Sleep(DispatchWait); Assert.IsTrue(dispatchDynamic); } finally { router.Stop(); } }
/// <summary> /// Constructs a <see cref="MsgRequestContext" /> for transactions that are within a session. /// </summary> /// <param name="session">The <see cref="DuplexSession" />.</param> /// <param name="query">The request <see cref="Msg" />.</param> /// <exception cref="ArgumentException">Thrown if the message passed does not have all of the headers necessary to be a request.</exception> internal MsgRequestContext(DuplexSession session, Msg query) { if (session == null) { throw new ArgumentNullException("session"); } if (query._FromEP == null) { throw new ArgumentException("Message cannot be a request: Null [_FromEP] header.", "requestMsg"); } if (query._SessionID == Guid.Empty) { throw new ArgumentException("Message cannot be a request: Empty [_SessionID] header.", "requestMsg"); } this.Header = query._ExtensionHeaders[MsgHeaderID.DuplexSession]; if (this.Header == null) { throw new ArgumentException("Message is not a DuplexSession query.", "requestMsg"); } this.router = session.Router; this.session = session; this.FromEP = query._FromEP.Clone(); this.SessionID = query._SessionID; #if TRACE this.TraceName = query.GetType().Name; #else this.TraceName = "(trace disabled)"; #endif }
private bool closed = false; // True if the transaction has completed /// <summary> /// Constructs a <see cref="MsgRequestContext" /> for transactions that are not within a session. /// </summary> /// <param name="router">The <see cref="MsgRouter" />.</param> /// <param name="requestMsg">The request <see cref="Msg" />.</param> /// <exception cref="ArgumentException">Thrown if the message passed does not have all of the headers necessary to be a request.</exception> internal MsgRequestContext(MsgRouter router, Msg requestMsg) { if (router == null) { throw new ArgumentNullException("router"); } if (requestMsg._FromEP == null) { throw new ArgumentException("Message cannot be a request: Null [_FromEP] header.", "requestMsg"); } if (requestMsg._SessionID == Guid.Empty) { throw new ArgumentException("Message cannot be a request: Empty [_SessionID] header.", "requestMsg"); } this.router = router; this.session = null; this.FromEP = requestMsg._FromEP.Clone(); this.SessionID = requestMsg._SessionID; #if TRACE this.TraceName = requestMsg.GetType().Name; #else this.TraceName = "(trace disabled)"; #endif }
private MsgEP instanceEP; // Endpoint of the connected sentinel instance. /// <summary> /// Constructor. /// </summary> /// <param name="router">The application message router to be used for communications.</param> /// <param name="serviceEP">The sentinel service endpoint.</param> public Sentinel(MsgRouter router, MsgEP serviceEP) { Global.RegisterMsgTypes(); this.router = router; this.serviceEP = serviceEP; this.instanceEP = null; }
public void LazyMessenger_DeliverTo_Cluster_Exception() { MsgRouter router = null; BasicTopology cluster = null; LazyMessenger messenger = null; DateTime start = DateTime.UtcNow; PropertyMsg query; try { router = CreateLeaf("detached", "hub", group); router.Dispatcher.AddTarget(this); cluster = new BasicTopology(); cluster.OpenClient(router, "logical://foo", null); messenger = new LazyMessenger(); messenger.OpenClient(router, "logical://confirm", null, new DeliveryConfirmCallback(OnDeliveryConfirmation)); Thread.Sleep(wait); query = new PropertyMsg(); query["operation"] = "exception"; query["data"] = "Hello World!"; query["query"] = "yes"; confirmation = null; try { messenger.Deliver(cluster, null, query, true); Assert.Fail("Exception expected"); } catch (Exception e) { Assert.AreEqual("Test Exception", e.Message); } } finally { if (messenger != null) { messenger.Close(); } if (cluster != null) { cluster.Close(); } if (router != null) { router.Stop(); } Config.SetConfig(null); } }