SPRNET-1300 - NMS custom schema is missing a way to set the Exception Listener when creating message listener containers

SPRNET-1302 - Add IErrorHandler interface to be called when exception thrown by message processing is not of vendor specific messaging exception type.
This commit is contained in:
markpollack
2009-12-16 19:00:48 +00:00
parent 5dbf70699e
commit 5b0ede6a7c
15 changed files with 537 additions and 28 deletions

View File

@@ -1428,6 +1428,26 @@ namespace MyApp
initialization. Default is true, optionally set to
false.</entry>
</row>
<row>
<entry>error-handler</entry>
<entry>A reference to a IErrorHandler that will handle any
uncaught exceptions other than those of the type NMSException
(in the case of ActiveMQ or EMSException int he case of TIBCO
EMS) that may occur during the execution of the message
listener. By default no ErrorHandler is registered and that
error-level logging is the default behavior.</entry>
</row>
<row>
<entry>exception-listener</entry>
<entry>A reference to an
Spring.Messaging.Nms.Core.IExceptionListener or
TIBCO.EMS.IExceptionListener as appropriate. Is invokved in case
of a NMSException or EMSException.</entry>
</row>
</tbody>
</tgroup>
</table>

View File

@@ -2070,11 +2070,6 @@
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Objects\Factory\Config\ICustomValueReferenceHolder.cs"
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Objects\Factory\Config\ConstructorArgumentValues.cs"
SubType = "Code"
@@ -2155,6 +2150,11 @@
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Objects\Factory\Config\ICustomValueReferenceHolder.cs"
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Objects\Factory\Config\IDestructionAwareObjectPostProcessor.cs"
SubType = "Code"
@@ -2967,6 +2967,11 @@
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Util\IErrorHandler.cs"
SubType = "Code"
BuildAction = "Compile"
/>
<File
RelPath = "Util\IEventExceptionsCollector.cs"
SubType = "Code"

View File

@@ -1093,6 +1093,7 @@
<Compile Include="Util\ConfigXmlElement.cs" />
<Compile Include="Util\FatalReflectionException.cs" />
<Compile Include="Util\IChainableConfigSystem.cs" />
<Compile Include="Util\IErrorHandler.cs" />
<Compile Include="Util\IEventExceptionsCollector.cs" />
<Compile Include="Util\IoUtils.cs" />
<Compile Include="Util\ITextPosition.cs" />

View File

@@ -1113,6 +1113,7 @@
<Compile Include="Util\ConfigXmlElement.cs" />
<Compile Include="Util\FatalReflectionException.cs" />
<Compile Include="Util\IChainableConfigSystem.cs" />
<Compile Include="Util\IErrorHandler.cs" />
<Compile Include="Util\IEventExceptionsCollector.cs" />
<Compile Include="Util\IoUtils.cs" />
<Compile Include="Util\ITextPosition.cs" />

View File

@@ -0,0 +1,42 @@
#region License
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed 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.
*/
#endregion
using System;
namespace Spring.Util
{
/// <summary>
/// A strategy for handling errors. This is especially useful for handling
/// errors that occur during asynchronous execution as in such cases it may not be
/// possible to throw the error to the original caller.
/// </summary>
/// <author>Mark Fisher</author>
/// <author>Mark Pollack (.NET)</author>
public interface IErrorHandler
{
/// <summary>
/// Handles the error.
/// </summary>
/// <param name="exception">The exception.</param>
void HandleError(Exception exception);
}
}

View File

@@ -94,6 +94,10 @@ namespace Spring.Messaging.Ems.Config
private readonly string PUBSUB_DOMAIN_ATTRIBUTE = "pubsub-domain";
private readonly string AUTO_STARTUP = "auto-startup";
private readonly string ERROR_HANDLER_ATTRIBUTE = "error-handler";
private readonly string EXCEPTION_LISTENER_ATTRIBUTE = "exception-listener";
#endregion
@@ -257,11 +261,26 @@ namespace Spring.Messaging.Ems.Config
containerDef.AddPropertyValue("ConnectionFactory", new RuntimeObjectReference(connectionFactoryObjectName));
string destinationResolverBeanName = containerElement.GetAttribute(DESTINATION_RESOLVER_ATTRIBUTE);
if (StringUtils.HasText(destinationResolverBeanName))
string errorHandlerObjectName = containerElement.GetAttribute(ERROR_HANDLER_ATTRIBUTE);
if (StringUtils.HasText(errorHandlerObjectName))
{
containerDef.AddPropertyValue("ErrorHandler",
new RuntimeObjectReference(errorHandlerObjectName));
}
string exceptionListenerObjectName = containerElement.GetAttribute(EXCEPTION_LISTENER_ATTRIBUTE);
if (StringUtils.HasText(exceptionListenerObjectName))
{
containerDef.AddPropertyValue("ExceptionListener",
new RuntimeObjectReference(exceptionListenerObjectName));
}
string destinationResolverObjectName = containerElement.GetAttribute(DESTINATION_RESOLVER_ATTRIBUTE);
if (StringUtils.HasText(destinationResolverObjectName))
{
containerDef.AddPropertyValue("DestinationResolver",
new RuntimeObjectReference(destinationResolverBeanName));
new RuntimeObjectReference(destinationResolverObjectName));
}
string acknowledge = containerElement.GetAttribute(ACKNOWLEDGE_ATTRIBUTE);

View File

@@ -83,7 +83,27 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-type" default="queue">
<xsd:attribute name="error-handler" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
A reference to an ErrorHandler strategy for handling any uncaught Exceptions
that may occur during the execution of the MessageListener.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="exception-listener" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
A reference to an Exception Listener strategy for handling any uncaught EMSExceptions
that may occur during the execution of the MessageListener.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-type" default="queue">
<xsd:annotation>
<xsd:documentation><![CDATA[
The destination type for this listener: "queue", "topic" or "durableTopic".

View File

@@ -55,6 +55,8 @@ namespace Spring.Messaging.Ems.Listener
private IExceptionListener exceptionListener;
private IErrorHandler errorHandler;
private bool exposeListenerSession = true;
private bool acceptMessagesWhileStopping = false;
@@ -207,6 +209,17 @@ namespace Spring.Messaging.Ems.Listener
set { exceptionListener = value; }
}
/// <summary>
/// Sets an ErrorHandler to be invoked in case of any uncaught exceptions thrown
/// while processing a Message. By default there will be no ErrorHandler
/// so that error-level logging is the only result.
/// </summary>
/// <value>The error handler.</value>
public IErrorHandler ErrorHandler
{
set { errorHandler = value; }
}
/// <summary>
/// Gets or sets a value indicating whether to expose listener session to a registered
@@ -545,8 +558,8 @@ namespace Spring.Messaging.Ems.Listener
if (Active)
{
// Regular case: failed while active.
// Log at error level.
logger.Error("Execution of EMS message listener failed", ex);
// Invoke ErrorHandler if available.
InvokeErrorHandler(ex);
}
else
{
@@ -556,6 +569,18 @@ namespace Spring.Messaging.Ems.Listener
}
}
protected virtual void InvokeErrorHandler(Exception exception)
{
if (errorHandler != null)
{
errorHandler.HandleError(exception);
}
else if (logger.IsWarnEnabled)
{
logger.Warn("Execution of EMS message listener failed, and no ErrorHandler has been set.", exception);
}
}
/// <summary>
/// Invokes the registered exception listener, if any.
/// </summary>

View File

@@ -21,7 +21,6 @@
using System;
using System.Xml;
using Apache.NMS;
using Spring.Core.TypeConversion;
using Spring.Core.TypeResolution;
using Spring.Messaging.Nms.Listener;
using Spring.Messaging.Nms.Listener.Adapter;
@@ -95,6 +94,10 @@ namespace Spring.Messaging.Nms.Config
private readonly string PUBSUB_DOMAIN_ATTRIBUTE = "pubsub-domain";
private readonly string AUTO_STARTUP = "auto-startup";
private readonly string ERROR_HANDLER_ATTRIBUTE = "error-handler";
private readonly string EXCEPTION_LISTENER_ATTRIBUTE = "exception-listener";
#endregion
@@ -260,11 +263,26 @@ namespace Spring.Messaging.Nms.Config
containerDef.AddPropertyValue("ConnectionFactory", new RuntimeObjectReference(connectionFactoryObjectName));
string destinationResolverBeanName = containerElement.GetAttribute(DESTINATION_RESOLVER_ATTRIBUTE);
if (StringUtils.HasText(destinationResolverBeanName))
string errorHandlerObjectName = containerElement.GetAttribute(ERROR_HANDLER_ATTRIBUTE);
if (StringUtils.HasText(errorHandlerObjectName))
{
containerDef.AddPropertyValue("ErrorHandler",
new RuntimeObjectReference(errorHandlerObjectName));
}
string exceptionListenerObjectName = containerElement.GetAttribute(EXCEPTION_LISTENER_ATTRIBUTE);
if (StringUtils.HasText(exceptionListenerObjectName))
{
containerDef.AddPropertyValue("ExceptionListener",
new RuntimeObjectReference(exceptionListenerObjectName));
}
string destinationResolverObjectName = containerElement.GetAttribute(DESTINATION_RESOLVER_ATTRIBUTE);
if (StringUtils.HasText(destinationResolverObjectName))
{
containerDef.AddPropertyValue("DestinationResolver",
new RuntimeObjectReference(destinationResolverBeanName));
new RuntimeObjectReference(destinationResolverObjectName));
}
string acknowledge = containerElement.GetAttribute(ACKNOWLEDGE_ATTRIBUTE);

View File

@@ -33,7 +33,7 @@ namespace Spring.Messaging.Nms.Config
NamespaceParser(
Namespace = "http://www.springframework.net/nms",
SchemaLocationAssemblyHint = typeof (NmsNamespaceParser),
SchemaLocation = "/Spring.Messaging.Nms.Config/spring-nms-1.2.xsd"
SchemaLocation = "/Spring.Messaging.Nms.Config/spring-nms-1.3.xsd"
)
]
public class NmsNamespaceParser : NamespaceParserSupport

View File

@@ -0,0 +1,250 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.net/nms"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:vs="http://schemas.microsoft.com/Visual-Studio-Intellisense"
targetNamespace="http://www.springframework.net/nms"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
vs:friendlyname="Spring.NET NMS Configuration" vs:ishtmlschema="false" vs:iscasesensitive="true" vs:requireattributequotes="true" vs:defaultnamespacequalifier="" vs:defaultnsprefix="">
<xsd:annotation>
<xsd:documentation><![CDATA[
Defines the configuration elements for the Spring Framework's NMS support.
Allows for configuring NMS listener containers in XML 'shortcut' style.
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="listener-container">
<xsd:annotation>
<xsd:documentation><![CDATA[
Each listener child element will be hosted by a container whose configuration
is determined by this parent element. This variant builds standard NMS
listener containers, operating against a specified NMS ConnectionFactory.
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="listener" type="listenerType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="container-type" default="simple">
<xsd:annotation>
<xsd:documentation><![CDATA[
The type of this listener container: "simple" is the only option as of Spring.NET 1.2
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="simple"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="container-custom-type" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
A custom listener container implementation class as fully qualified type name.
Default is Spring's SimpleMessageListenerContainer.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="auto-startup" type="xsd:boolean" default="true">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Set whether to automatically start the listeners after initialization. Default is true, optionally set to false.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="connection-factory" type="xsd:string" default="ConnectionFactory">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the NMS ConnectionFactory object.
Default is "ConnectionFactory".
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-resolver" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the DestinationResolver strategy for resolving destination names.
Default is a DynamicDestinationResolver, using the NMS provider's queue/topic
name resolution.]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="message-converter" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
A reference to the MessageConverter strategy for converting NMS Messages to
listener method arguments. Default is a SimpleMessageConverter.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-handler" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
A reference to an ErrorHandler strategy for handling any uncaught Exceptions
that may occur during the execution of the MessageListener.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="exception-listener" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
A reference to an Exception Listener strategy for handling any uncaught NMSExceptions
that may occur during the execution of the MessageListener.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-type" default="queue">
<xsd:annotation>
<xsd:documentation><![CDATA[
The NMS destination type for this listener: "queue", "topic" or "durableTopic".
The default is "queue".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="queue"/>
<xsd:enumeration value="topic"/>
<xsd:enumeration value="durableTopic"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="client-id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The NMS client id for this listener container.
Needs to be specified when using durable subscriptions.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="acknowledge" default="auto">
<xsd:annotation>
<xsd:documentation><![CDATA[
The native NMS acknowledge mode: "auto", "client", "dups-ok" or "transacted".
A value of "transacted" effectively activates a locally transacted Session;
as alternative, specify an external "transaction-manager" via the corresponding
attribute. Default is "auto".
]]></xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:NMTOKEN">
<xsd:enumeration value="auto"/>
<xsd:enumeration value="client"/>
<xsd:enumeration value="dups-ok"/>
<xsd:enumeration value="transacted"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="concurrency" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The number of concurrent sessions/consumers to start for each listener.
Default is 1; keep concurrency limited to 1 in case of a topic listener
or if message ordering is important; consider raising it for general queues.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="recovery-interval" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The TimeSpan interval to sleep in between connection recovery attemtps. Default is 5 seconds.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="max-recovery-time" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
The TimeSpan interval in which connection recovery attempts will be made. Default is 10 minutes.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="listenerType">
<xsd:attribute name="id" type="xsd:ID">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for a listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The destination name for this listener, resolved through the
container-wide IDestinationResolver strategy (if any). Required.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="subscription" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name for the durable subscription, if any.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="selector" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The NMS message selector for this listener.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ref" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation><![CDATA[
The object name of the listener object, implementing
the IMessageListener/ISessionAwareMessageListener interface
or defining the specified listener method. Required.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="method" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the listener method to invoke. If not specified,
the target object is supposed to implement the IMessageListener
or ISessionAwareMessageListener interface.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="response-destination" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the default response destination to send response messages to.
This will be applied in case of a request message that does not carry
a "NMSReplyTo" field. The type of this destination will be determined
by the listener-container's "destination-type" attribute.
Note: This only applies to a listener method with a return value,
for which each result object will be converted into a response message.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="pubsub-domain" type="xsd:boolean" use="optional" default="false">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
Set to true for the publish-subscribe domain (Topics) or false (the default) for the
point-to-point domain (Queues).
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:schema>

View File

@@ -56,6 +56,8 @@ namespace Spring.Messaging.Nms.Listener
private IExceptionListener exceptionListener;
private IErrorHandler errorHandler;
private bool exposeListenerISession = true;
private bool acceptMessagesWhileStopping = false;
@@ -208,6 +210,17 @@ namespace Spring.Messaging.Nms.Listener
set { exceptionListener = value; }
}
/// <summary>
/// Sets an ErrorHandler to be invoked in case of any uncaught exceptions thrown
/// while processing a Message. By default there will be no ErrorHandler
/// so that error-level logging is the only result.
/// </summary>
/// <value>The error handler.</value>
public IErrorHandler ErrorHandler
{
set { errorHandler = value; }
}
/// <summary>
/// Gets or sets a value indicating whether to expose listener session to a registered
@@ -544,8 +557,8 @@ namespace Spring.Messaging.Nms.Listener
if (Active)
{
// Regular case: failed while active.
// Log at error level.
logger.Error("Execution of NMS message listener failed", ex);
// Invoke ErrorHandler if available.
InvokeErrorHandler(ex);
}
else
{
@@ -555,6 +568,22 @@ namespace Spring.Messaging.Nms.Listener
}
}
/// <summary>
/// Invokes the error handler.
/// </summary>
/// <param name="exception">The exception.</param>
protected virtual void InvokeErrorHandler(Exception exception)
{
if (errorHandler != null)
{
errorHandler.HandleError(exception);
}
else if(logger.IsWarnEnabled)
{
logger.Warn("Execution of NMS message listener failed, and no ErrorHandler has been set.", exception);
}
}
/// <summary>
/// Invokes the registered exception listener, if any.
/// </summary>
@@ -568,6 +597,8 @@ namespace Spring.Messaging.Nms.Listener
exListener.OnException(ex);
}
}
#endregion

View File

@@ -119,6 +119,9 @@
</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Messaging\Nms\Config\spring-nms-1.3.xsd" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>

View File

@@ -26,6 +26,7 @@ using NUnit.Framework;
using Rhino.Mocks;
using Spring.Messaging.Ems.Common;
using Spring.Messaging.Ems.Listener;
using Spring.Util;
using TIBCO.EMS;
#endregion
@@ -99,17 +100,56 @@ namespace Spring.Messaging.Ems.Core
// manually trigger an Exception with the above bad MessageListener...
messageConsumer.SendMessage(message);
mocks.VerifyAll();
}
[Test]
public void RegisteredErrorHandlerIsInvokedOnException()
{
SimpleMessageConsumer messageConsumer = new SimpleMessageConsumer();
ISession session = (ISession)mocks.CreateMock(typeof(ISession));
Expect.Call(session.CreateQueue(DESTINATION_NAME)).Return(QUEUE_DESTINATION);
Expect.Call(session.CreateConsumer(QUEUE_DESTINATION, null)).Return(messageConsumer);
// an exception is thrown, so the rollback logic is being applied here...
Expect.Call(session.Transacted).Return(false);
IConnection connection = (IConnection)mocks.CreateMock(typeof(IConnection));
//connection.ExceptionListener += container.OnException;
connection.ExceptionListener = container;
Expect.Call(connection.CreateSession(false, container.SessionAcknowledgeMode)).Return(session);
connection.Start();
IConnectionFactory connectionFactory = (IConnectionFactory)mocks.CreateMock(typeof(IConnectionFactory));
Expect.Call(connectionFactory.CreateConnection()).Return(connection);
IllegalStateException theException = new IllegalStateException(EXCEPTION_MESSAGE);
IErrorHandler errorHandler = (IErrorHandler)mocks.CreateMock(typeof(IErrorHandler));
errorHandler.HandleError(theException);
//IMessage message = (IMessage) mocks.CreateMock(typeof (IMessage));
TextMessage message = new TextMessage(null, "hello");
mocks.ReplayAll();
container.ConnectionFactory = connectionFactory;
container.DestinationName = DESTINATION_NAME;
container.MessageListener = new BadSessionAwareMessageListener(theException);
container.ErrorHandler = errorHandler;
container.AfterPropertiesSet();
// manually trigger an Exception with the above bad MessageListener...
messageConsumer.SendMessage(message);
mocks.VerifyAll();
}
}
internal class BadSessionAwareMessageListener : ISessionAwareMessageListener
{
private EMSException exception;

View File

@@ -25,6 +25,7 @@ using Apache.NMS;
using NUnit.Framework;
using Rhino.Mocks;
using Spring.Messaging.Nms.Listener;
using Spring.Util;
#endregion
@@ -94,15 +95,48 @@ namespace Spring.Messaging.Nms.Core
// manually trigger an Exception with the above bad MessageListener...
messageConsumer.SendMessage(message);
mocks.VerifyAll();
}
[Test]
public void RegisteredErrorHandlerIsInvokedOnException()
{
SimpleMessageConsumer messageConsumer = new SimpleMessageConsumer();
ISession session = (ISession)mocks.CreateMock(typeof(ISession));
Expect.Call(session.GetQueue(DESTINATION_NAME)).Return(QUEUE_DESTINATION);
Expect.Call(session.CreateConsumer(QUEUE_DESTINATION, null)).Return(messageConsumer);
// an exception is thrown, so the rollback logic is being applied here...
Expect.Call(session.Transacted).Return(false);
IConnection connection = (IConnection)mocks.CreateMock(typeof(IConnection));
connection.ExceptionListener += container.OnException;
Expect.Call(connection.CreateSession(container.SessionAcknowledgeMode)).Return(session);
connection.Start();
IConnectionFactory connectionFactory = (IConnectionFactory)mocks.CreateMock(typeof(IConnectionFactory));
Expect.Call(connectionFactory.CreateConnection()).Return(connection);
IllegalStateException theException = new IllegalStateException(EXCEPTION_MESSAGE);
IErrorHandler errorHandler = (IErrorHandler)mocks.CreateMock(typeof(IErrorHandler));
errorHandler.HandleError(theException);
IMessage message = (IMessage)mocks.CreateMock(typeof(IMessage));
mocks.ReplayAll();
container.ConnectionFactory = connectionFactory;
container.DestinationName = DESTINATION_NAME;
container.MessageListener = new BadSessionAwareMessageListener(theException);
container.ErrorHandler = errorHandler;
container.AfterPropertiesSet();
// manually trigger an Exception with the above bad MessageListener...
messageConsumer.SendMessage(message);
mocks.VerifyAll();
}
}