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:
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
42
src/Spring/Spring.Core/Util/IErrorHandler.cs
Normal file
42
src/Spring/Spring.Core/Util/IErrorHandler.cs
Normal 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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".
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -119,6 +119,9 @@
|
||||
</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Messaging\Nms\Config\spring-nms-1.3.xsd" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user