NMS development
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,367 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
using System.Xml;
|
||||
using Apache.NMS;
|
||||
using Spring.Messaging.Nms.Listener;
|
||||
using Spring.Messaging.Nms.Listener.Adapter;
|
||||
using Spring.Objects.Factory.Config;
|
||||
using Spring.Objects.Factory.Support;
|
||||
using Spring.Objects.Factory.Xml;
|
||||
using Spring.Util;
|
||||
|
||||
namespace Spring.Messaging.Nms.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Parser for the NMS <code><listener-container></code> element.
|
||||
/// </summary>
|
||||
/// <author>Mark Fisher</author>
|
||||
/// <author>Juergen Hoeller</author>
|
||||
/// <author>Mark Pollack (.NET)</author>
|
||||
public class MessageListenerContainerObjectDefinitionParser : IObjectDefinitionParser
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly string LISTENER_ELEMENT = "listener";
|
||||
|
||||
private readonly string ID_ATTRIBUTE = "id";
|
||||
|
||||
private readonly string DESTINATION_ATTRIBUTE = "destination";
|
||||
|
||||
private readonly string SUBSCRIPTION_ATTRIBUTE = "subscription";
|
||||
|
||||
private readonly string SELECTOR_ATTRIBUTE = "selector";
|
||||
|
||||
private readonly string REF_ATTRIBUTE = "ref";
|
||||
|
||||
private readonly string METHOD_ATTRIBUTE = "method";
|
||||
|
||||
private readonly string DESTINATION_RESOLVER_ATTRIBUTE = "destination-resolver";
|
||||
|
||||
private readonly string MESSAGE_CONVERTER_ATTRIBUTE = "message-converter";
|
||||
|
||||
private readonly string RESPONSE_DESTINATION_ATTRIBUTE = "response-destination";
|
||||
|
||||
private readonly string DESTINATION_TYPE_ATTRIBUTE = "destination-type";
|
||||
|
||||
private readonly string DESTINATION_TYPE_QUEUE = "queue";
|
||||
|
||||
private readonly string DESTINATION_TYPE_TOPIC = "topic";
|
||||
|
||||
private readonly string DESTINATION_TYPE_DURABLE_TOPIC = "durableTopic";
|
||||
|
||||
private readonly string CLIENT_ID_ATTRIBUTE = "client-id";
|
||||
|
||||
private readonly string ACKNOWLEDGE_ATTRIBUTE = "acknowledge";
|
||||
|
||||
private readonly string ACKNOWLEDGE_AUTO = "auto";
|
||||
|
||||
private readonly string ACKNOWLEDGE_CLIENT = "client";
|
||||
|
||||
private readonly string ACKNOWLEDGE_DUPS_OK = "dups-ok";
|
||||
|
||||
private readonly string ACKNOWLEDGE_TRANSACTED = "transacted";
|
||||
|
||||
private readonly string CONCURRENCY_ATTRIBUTE = "concurrency";
|
||||
|
||||
private readonly string CONNECTION_FACTORY_ATTRIBUTE = "connection-factory";
|
||||
|
||||
#endregion
|
||||
|
||||
#region IObjectDefinitionParser Members
|
||||
|
||||
/// <summary>
|
||||
/// Parse the specified XmlElement and register the resulting
|
||||
/// ObjectDefinitions with the <see cref="ParserContext.Registry"/> IObjectDefinitionRegistry
|
||||
/// embedded in the supplied <see cref="ParserContext"/>
|
||||
/// </summary>
|
||||
/// <param name="element">The element to be parsed.</param>
|
||||
/// <param name="parserContext">TThe object encapsulating the current state of the parsing process.
|
||||
/// Provides access to a IObjectDefinitionRegistry</param>
|
||||
/// <returns>The primary object definition.</returns>
|
||||
/// <remarks>
|
||||
/// <p>
|
||||
/// This method is never invoked if the parser is namespace aware
|
||||
/// and was called to process the root node.
|
||||
/// </p>
|
||||
/// </remarks>
|
||||
public IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext)
|
||||
{
|
||||
|
||||
XmlNodeList childNodes = element.ChildNodes;
|
||||
foreach (XmlNode childNode in childNodes)
|
||||
{
|
||||
if (childNode.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
string localName = childNode.LocalName;
|
||||
if (LISTENER_ELEMENT.Equals(localName))
|
||||
{
|
||||
ParseListener((XmlElement) childNode, element, parserContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ParseListener(XmlElement listenerElement, XmlElement containerElement, ParserContext parserContext)
|
||||
{
|
||||
ObjectDefinitionBuilder listenerDefBuilder =
|
||||
parserContext.ParserHelper.CreateRootObjectDefinitionBuilder(typeof (MessageListenerAdapter));
|
||||
|
||||
string reference = listenerElement.GetAttribute(REF_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(reference))
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(listenerElement, LISTENER_ELEMENT,
|
||||
"Listener '" + REF_ATTRIBUTE +
|
||||
"' attribute contains empty value.");
|
||||
}
|
||||
listenerDefBuilder.AddPropertyValue("HandlerObject", new RuntimeObjectReference(reference));
|
||||
|
||||
string handlerMethod = null;
|
||||
if (listenerElement.HasAttribute(METHOD_ATTRIBUTE))
|
||||
{
|
||||
handlerMethod = listenerElement.GetAttribute(METHOD_ATTRIBUTE);
|
||||
{
|
||||
if (!StringUtils.HasText(handlerMethod))
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(listenerElement, LISTENER_ELEMENT,
|
||||
"Listener '" + METHOD_ATTRIBUTE +
|
||||
"' attribute contains empty value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
listenerDefBuilder.AddPropertyValue("DefaultHandlerMethod", handlerMethod);
|
||||
|
||||
if (containerElement.HasAttribute(MESSAGE_CONVERTER_ATTRIBUTE))
|
||||
{
|
||||
string messageConverter = containerElement.GetAttribute(MESSAGE_CONVERTER_ATTRIBUTE);
|
||||
listenerDefBuilder.AddPropertyValue("MessageConverter", new RuntimeObjectReference(messageConverter));
|
||||
}
|
||||
|
||||
ObjectDefinitionBuilder containerDefBuilder = ParseContainer(listenerElement, containerElement, parserContext);
|
||||
|
||||
if (listenerElement.HasAttribute(RESPONSE_DESTINATION_ATTRIBUTE))
|
||||
{
|
||||
string responseDestination = listenerElement.GetAttribute(RESPONSE_DESTINATION_ATTRIBUTE);
|
||||
bool pubSubDomain = IndicatesPubSub(containerDefBuilder.RawObjectDefinition);
|
||||
listenerDefBuilder.AddPropertyValue(pubSubDomain ? "DefaultResponseTopicName" : "DefaultResponseQueueName",
|
||||
responseDestination);
|
||||
if (containerDefBuilder.RawObjectDefinition.PropertyValues.Contains("DestinationResolver"))
|
||||
{
|
||||
listenerDefBuilder.AddPropertyValue("DestinationResolver",
|
||||
containerDefBuilder.RawObjectDefinition.PropertyValues.GetPropertyValue
|
||||
(
|
||||
"DestinationResolver").Value);
|
||||
}
|
||||
}
|
||||
|
||||
containerDefBuilder.AddPropertyValue("MessageListener", listenerDefBuilder.ObjectDefinition);
|
||||
|
||||
string containerObjectName = listenerElement.GetAttribute(ID_ATTRIBUTE);
|
||||
// If no object id is given auto generate one using the ReaderContext's ObjectNameGenerator
|
||||
if (!StringUtils.HasText(containerObjectName))
|
||||
{
|
||||
containerObjectName =
|
||||
parserContext.ReaderContext.GenerateObjectName(containerDefBuilder.RawObjectDefinition);
|
||||
}
|
||||
|
||||
parserContext.Registry.RegisterObjectDefinition(containerObjectName, containerDefBuilder.ObjectDefinition);
|
||||
}
|
||||
|
||||
private ObjectDefinitionBuilder ParseContainer(XmlElement listenerElement, XmlElement containerElement,
|
||||
ParserContext parserContext)
|
||||
{
|
||||
//Only support SimpleMessageListenerContainer
|
||||
ObjectDefinitionBuilder containerDef =
|
||||
parserContext.ParserHelper.CreateRootObjectDefinitionBuilder(typeof (SimpleMessageListenerContainer));
|
||||
|
||||
ParseListenerConfiguration(listenerElement, parserContext, containerDef);
|
||||
ParseContainerConfiguration(containerElement, parserContext, containerDef);
|
||||
|
||||
string connectionFactoryObjectName = "connectionFactory";
|
||||
if (containerElement.HasAttribute(CONNECTION_FACTORY_ATTRIBUTE))
|
||||
{
|
||||
connectionFactoryObjectName = containerElement.GetAttribute(CONNECTION_FACTORY_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(connectionFactoryObjectName))
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(listenerElement, LISTENER_ELEMENT,
|
||||
"Listener container '" + CONNECTION_FACTORY_ATTRIBUTE +
|
||||
"' attribute contains empty value.");
|
||||
}
|
||||
}
|
||||
|
||||
containerDef.AddPropertyValue("ConnectionFactory", new RuntimeObjectReference(connectionFactoryObjectName));
|
||||
|
||||
string destinationResolverBeanName = containerElement.GetAttribute(DESTINATION_RESOLVER_ATTRIBUTE);
|
||||
if (StringUtils.HasText(destinationResolverBeanName))
|
||||
{
|
||||
containerDef.AddPropertyValue("DestinationResolver",
|
||||
new RuntimeObjectReference(destinationResolverBeanName));
|
||||
}
|
||||
|
||||
string acknowledge = containerElement.GetAttribute(ACKNOWLEDGE_ATTRIBUTE);
|
||||
if (StringUtils.HasText(acknowledge))
|
||||
{
|
||||
AcknowledgementMode acknowledgementMode = ParseAcknowledgementMode(containerElement, parserContext);
|
||||
containerDef.AddPropertyValue("SessionAcknowledgeMode", acknowledgementMode);
|
||||
}
|
||||
|
||||
int[] concurrency = ParseConcurrency(containerElement, parserContext);
|
||||
if (concurrency != null)
|
||||
{
|
||||
containerDef.AddPropertyValue("ConcurrentConsumers", concurrency[1]);
|
||||
}
|
||||
|
||||
return containerDef;
|
||||
}
|
||||
|
||||
private bool IndicatesPubSub(AbstractObjectDefinition configDef)
|
||||
{
|
||||
return (bool) configDef.PropertyValues.GetPropertyValue("PubSubDomain").Value;
|
||||
}
|
||||
|
||||
private void ParseListenerConfiguration(XmlElement ele, ParserContext parserContext,
|
||||
ObjectDefinitionBuilder containerDef)
|
||||
{
|
||||
string destination = ele.GetAttribute(DESTINATION_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(destination))
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(ele, LISTENER_ELEMENT,
|
||||
"Listener '" + DESTINATION_ATTRIBUTE +
|
||||
"' attribute contains empty value.");
|
||||
}
|
||||
containerDef.AddPropertyValue("DestinationName", destination);
|
||||
|
||||
if (ele.HasAttribute(SUBSCRIPTION_ATTRIBUTE))
|
||||
{
|
||||
string subscription = ele.GetAttribute(SUBSCRIPTION_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(subscription))
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(ele, SUBSCRIPTION_ATTRIBUTE,
|
||||
"Listener '" + SUBSCRIPTION_ATTRIBUTE +
|
||||
"' attribute contains empty value.");
|
||||
}
|
||||
containerDef.AddPropertyValue("DurableSubscriptionName", subscription);
|
||||
}
|
||||
|
||||
if (ele.HasAttribute(SELECTOR_ATTRIBUTE))
|
||||
{
|
||||
string selector = ele.GetAttribute(SELECTOR_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(selector))
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(ele, selector,
|
||||
"Listener '" + SELECTOR_ATTRIBUTE +
|
||||
"' attribute contains empty value.");
|
||||
}
|
||||
containerDef.AddPropertyValue("MessageSelector", selector);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseContainerConfiguration(XmlElement ele, ParserContext parserContext,
|
||||
ObjectDefinitionBuilder containerDef)
|
||||
{
|
||||
string destinationType = ele.GetAttribute(DESTINATION_TYPE_ATTRIBUTE);
|
||||
bool pubSubDomain = false;
|
||||
bool subscriptionDurable = false;
|
||||
if (DESTINATION_TYPE_DURABLE_TOPIC.Equals(destinationType))
|
||||
{
|
||||
pubSubDomain = true;
|
||||
subscriptionDurable = true;
|
||||
}
|
||||
else if (DESTINATION_TYPE_TOPIC.Equals(destinationType))
|
||||
{
|
||||
pubSubDomain = true;
|
||||
}
|
||||
else if ("".Equals(destinationType) || DESTINATION_TYPE_QUEUE.Equals(destinationType))
|
||||
{
|
||||
// the default: queue
|
||||
}
|
||||
else
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(ele, destinationType,
|
||||
"Invalid listener container '" + DESTINATION_TYPE_ATTRIBUTE +
|
||||
"': only 'queue', 'topic' and 'durableTopic' supported");
|
||||
}
|
||||
|
||||
containerDef.AddPropertyValue("PubSubDomain", pubSubDomain);
|
||||
containerDef.AddPropertyValue("SubscriptionDurable", subscriptionDurable);
|
||||
|
||||
if (ele.HasAttribute(CLIENT_ID_ATTRIBUTE))
|
||||
{
|
||||
string clientId = ele.GetAttribute(CLIENT_ID_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(clientId))
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(ele, clientId,
|
||||
"Listener '" + CLIENT_ID_ATTRIBUTE +
|
||||
"' attribute contains empty value.");
|
||||
}
|
||||
containerDef.AddPropertyValue("ClientId", clientId);
|
||||
}
|
||||
}
|
||||
|
||||
private AcknowledgementMode ParseAcknowledgementMode(XmlElement element, ParserContext parserContext)
|
||||
{
|
||||
string acknowledge = element.GetAttribute(ACKNOWLEDGE_ATTRIBUTE);
|
||||
if (acknowledge.Equals(ACKNOWLEDGE_TRANSACTED))
|
||||
{
|
||||
return AcknowledgementMode.Transactional;
|
||||
}
|
||||
else if (acknowledge.Equals(ACKNOWLEDGE_DUPS_OK))
|
||||
{
|
||||
return AcknowledgementMode.DupsOkAcknowledge;
|
||||
}
|
||||
else if (acknowledge.Equals(ACKNOWLEDGE_CLIENT))
|
||||
{
|
||||
return AcknowledgementMode.ClientAcknowledge;
|
||||
}
|
||||
else if (!acknowledge.Equals(ACKNOWLEDGE_AUTO))
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(element, ACKNOWLEDGE_ATTRIBUTE,
|
||||
"Invalid listener container 'acknowledge' setting ['" +
|
||||
acknowledge +
|
||||
"]: only \"auto\", \"client\", \"dups-ok\" and \"transacted\" supported.");
|
||||
}
|
||||
return AcknowledgementMode.AutoAcknowledge;
|
||||
}
|
||||
|
||||
private int[] ParseConcurrency(XmlElement ele, ParserContext parserContext)
|
||||
{
|
||||
String concurrency = ele.GetAttribute(CONCURRENCY_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(concurrency))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return new int[] {1, Int32.Parse(concurrency)};
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(ele, CONCURRENCY_ATTRIBUTE,
|
||||
"Invalid concurrency value [" + concurrency + "]: only " +
|
||||
"integer (e.g. \"5\") values upported.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
using Spring.Objects.Factory.Xml;
|
||||
|
||||
namespace Spring.Messaging.Nms.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Namespace parser for the nms namespace.
|
||||
/// </summary>
|
||||
/// <author>Mark Fisher</author>
|
||||
/// <author>Juergen Hoeller</author>
|
||||
/// <author>Mark Pollack (.NET)</author>
|
||||
[
|
||||
NamespaceParser(
|
||||
Namespace = "http://www.springframework.net/nms",
|
||||
SchemaLocationAssemblyHint = typeof (NmsNamespaceParser),
|
||||
SchemaLocation = "/Spring.Messaging.Nms.Config/spring-nms-1.2.xsd"
|
||||
)
|
||||
]
|
||||
public class NmsNamespaceParser : NamespaceParserSupport
|
||||
{
|
||||
/// <summary>
|
||||
/// Register a MessageListenerContainer for the '<code>listener-container</code>' tag.
|
||||
/// </summary>
|
||||
public override void Init()
|
||||
{
|
||||
RegisterObjectDefinitionParser("listener-container", new MessageListenerContainerObjectDefinitionParser());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<?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="connection-factory" type="xsd:string" default="connectionFactory">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to the NMS ConnectionFactory bean.
|
||||
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="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: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:complexType>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -382,10 +382,10 @@ namespace Spring.Messaging.Nms.Connections
|
||||
/// <returns>the wrapped connection</returns>
|
||||
protected virtual IConnection GetSharedConnection(IConnection target)
|
||||
{
|
||||
lock (connectionMonitor)
|
||||
lock (connectionMonitor)
|
||||
{
|
||||
return new CloseSupressingConnection(this, target);
|
||||
}
|
||||
return new CloseSupressingConnection(this, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,7 +461,7 @@ namespace Spring.Messaging.Nms.Connections
|
||||
{
|
||||
return session;
|
||||
}
|
||||
return target.CreateSession();
|
||||
return target.CreateSession(acknowledgementMode);
|
||||
}
|
||||
|
||||
#region Pass through implementations to the target connection
|
||||
|
||||
@@ -52,6 +52,20 @@ namespace Spring.Messaging.Nms
|
||||
/// </returns>
|
||||
/// <throws>NMSException if there is any problem </throws>
|
||||
object Execute(ISessionCallback action);
|
||||
|
||||
/// <summary> Execute the action specified by the given action object within
|
||||
/// a NMS Session.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Note that the value of PubSubDomain affects the behavior of this method.
|
||||
/// If PubSubDomain equals true, then a Session is passed to the callback.
|
||||
/// If false, then a ISession is passed to the callback.</para>b
|
||||
/// </remarks>
|
||||
/// <param name="del">delegate that exposes the session</param>
|
||||
/// <returns> the result object from working with the session
|
||||
/// </returns>
|
||||
/// <throws>NMSException if there is any problem </throws>
|
||||
object Execute(SessionDelegate del);
|
||||
|
||||
/// <summary> Send a message to a NMS destination. The callback gives access to
|
||||
/// the NMS session and MessageProducer in order to do more complex
|
||||
|
||||
@@ -26,8 +26,9 @@ namespace Spring.Messaging.Nms
|
||||
/// Session
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <p>To be used with the MessageTemplate.Execute(ISessionCallback)}
|
||||
/// method, often implemented as an anonymous inner class.</p>
|
||||
/// <para>To be used with the MessageTemplate.Execute(ISessionCallback)}
|
||||
/// method. See <see cref="SessionDelegate"/> for the equivalent callback
|
||||
/// that can be used as a (anonymous) delegate.</para>
|
||||
/// </remarks>
|
||||
/// <author>Mark Pollack</author>
|
||||
/// <seealso cref="MessageTemplate.Execute(ISessionCallback,bool)">
|
||||
|
||||
@@ -24,7 +24,7 @@ using Common.Logging;
|
||||
using Spring.Context;
|
||||
using Spring.Messaging.Nms.Connections;
|
||||
using Spring.Messaging.Nms.Support;
|
||||
using Spring.Messaging.Nms.Support.IDestinations;
|
||||
using Spring.Messaging.Nms.Support.Destinations;
|
||||
using Spring.Objects.Factory;
|
||||
|
||||
namespace Spring.Messaging.Nms.Listener
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
using Apache.NMS;
|
||||
|
||||
namespace Spring.Messaging.Nms.Listener.Adapter
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception to be thrown when the execution of a listener method failed.
|
||||
/// </summary>
|
||||
/// <author>Juergen Hoeller</author>
|
||||
/// <author>Mark Pollack (.NET)</author>
|
||||
public class ListenerExecutionFailedException : NMSException
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ListenerExecutionFailedException"/> class, with the specified message
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
public ListenerExecutionFailedException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ListenerExecutionFailedException"/> class, with the specified message
|
||||
/// and root cause exception
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="innerException">The inner exception.</param>
|
||||
public ListenerExecutionFailedException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,33 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using Common.Logging;
|
||||
using Spring.Expressions;
|
||||
using Spring.Messaging.Nms.Listener;
|
||||
using Spring.Messaging.Nms.Support;
|
||||
using Spring.Messaging.Nms.Support.Converter;
|
||||
using Spring.Messaging.Nms.Support.IDestinations;
|
||||
using Spring.Messaging.Nms.Support.Destinations;
|
||||
using Spring.Util;
|
||||
using Apache.NMS;
|
||||
|
||||
@@ -146,7 +168,7 @@ namespace Spring.Messaging.Nms.Listener.Adapter
|
||||
/// <para>Alternatively, specify a JMS Destination object as "defaultResponseDestination".</para>
|
||||
/// </summary>
|
||||
/// <value>The name of the default response destination queue.</value>
|
||||
public string DefaultResponseDestinationQueueName
|
||||
public string DefaultResponseQueueName
|
||||
{
|
||||
set { defaultResponseDestination = new DestinationNameHolder(value, false); }
|
||||
}
|
||||
@@ -158,7 +180,7 @@ namespace Spring.Messaging.Nms.Listener.Adapter
|
||||
/// <para>Alternatively, specify a JMS Destination object as "defaultResponseDestination".</para>
|
||||
/// </summary>
|
||||
/// <value>The name of the default response destination topic.</value>
|
||||
public string DefaultResponseDestinationTopicName
|
||||
public string DefaultResponseTopicName
|
||||
{
|
||||
set { defaultResponseDestination = new DestinationNameHolder(value, true); }
|
||||
}
|
||||
@@ -238,6 +260,29 @@ namespace Spring.Messaging.Nms.Listener.Adapter
|
||||
/// <param name="session">The session to operate on.</param>
|
||||
public void OnMessage(IMessage message, ISession session)
|
||||
{
|
||||
if (handlerObject != this)
|
||||
{
|
||||
if (typeof(ISessionAwareMessageListener).IsInstanceOfType(handlerObject))
|
||||
{
|
||||
if (session != null)
|
||||
{
|
||||
((ISessionAwareMessageListener) handlerObject).OnMessage(message, session);
|
||||
return;
|
||||
}
|
||||
else if (!typeof(IMessageListener).IsInstanceOfType(handlerObject))
|
||||
{
|
||||
throw new InvalidOperationException("MessageListenerAdapter cannot handle a " +
|
||||
"SessionAwareMessageListener delegate if it hasn't been invoked with a Session itself");
|
||||
}
|
||||
}
|
||||
if (typeof(IMessageListener).IsInstanceOfType(handlerObject))
|
||||
{
|
||||
((IMessageListener)handlerObject).OnMessage(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Regular case: find a handler method reflectively.
|
||||
object convertedMessage = ExtractMessage(message);
|
||||
|
||||
|
||||
@@ -250,7 +295,34 @@ namespace Spring.Messaging.Nms.Listener.Adapter
|
||||
processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
|
||||
|
||||
//Invoke message handler method and get result.
|
||||
object result = processingExpression.GetValue(handlerObject, vars);
|
||||
object result;
|
||||
try
|
||||
{
|
||||
result = processingExpression.GetValue(handlerObject, vars);
|
||||
}
|
||||
catch (NMSException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
// Will only happen if dynamic method invocation falls back to standard reflection.
|
||||
catch (TargetInvocationException ex)
|
||||
{
|
||||
Exception targetEx = ex.InnerException;
|
||||
if (ObjectUtils.IsAssignable(typeof(NMSException), targetEx))
|
||||
{
|
||||
throw ReflectionUtils.UnwrapTargetInvocationException(ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ListenerExecutionFailedException("Listener method '" + defaultHandlerMethod + "' threw exception", targetEx);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ListenerExecutionFailedException("Failed to invoke target method '" + defaultHandlerMethod +
|
||||
"' with argument " + convertedMessage, ex);
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
HandleResult(result, message, session);
|
||||
@@ -478,9 +550,9 @@ namespace Spring.Messaging.Nms.Listener.Adapter
|
||||
/// </summary>
|
||||
internal class DestinationNameHolder
|
||||
{
|
||||
private string name;
|
||||
private readonly string name;
|
||||
|
||||
private bool isTopic;
|
||||
private readonly bool isTopic;
|
||||
|
||||
public DestinationNameHolder(string name, bool isTopic)
|
||||
{
|
||||
|
||||
@@ -223,6 +223,7 @@ namespace Spring.Messaging.Nms.Listener
|
||||
|
||||
|
||||
SimpleMessageListener listener = new SimpleMessageListener(this, session);
|
||||
// put in explicit registration with 'new' for compilation on .NET 1.1
|
||||
consumer.Listener += new Apache.NMS.MessageListener(listener.OnMessage);
|
||||
return consumer;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ using Common.Logging;
|
||||
using Spring.Messaging.Nms.Connections;
|
||||
using Spring.Messaging.Nms.Support;
|
||||
using Spring.Messaging.Nms.Support.Converter;
|
||||
using Spring.Messaging.Nms.Support.IDestinations;
|
||||
using Spring.Messaging.Nms.Support.Destinations;
|
||||
using Spring.Transaction.Support;
|
||||
using Spring.Util;
|
||||
using Apache.NMS;
|
||||
@@ -581,6 +581,25 @@ namespace Spring.Messaging.Nms
|
||||
|
||||
#region IMessageOperations Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Execute the action specified by the given action object within
|
||||
/// a NMS Session.
|
||||
/// </summary>
|
||||
/// <param name="del">delegate that exposes the session</param>
|
||||
/// <returns>
|
||||
/// the result object from working with the session
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>Note that the value of PubSubDomain affects the behavior of this method.
|
||||
/// If PubSubDomain equals true, then a Session is passed to the callback.
|
||||
/// If false, then a ISession is passed to the callback.</para>b
|
||||
/// </remarks>
|
||||
/// <throws>NMSException if there is any problem </throws>
|
||||
public object Execute(SessionDelegate del)
|
||||
{
|
||||
return Execute(new ExecuteSessionCallbackUsingDelegate(del));
|
||||
}
|
||||
|
||||
/// <summary> Execute the action specified by the given action object within
|
||||
/// a NMS Session.
|
||||
/// <p>Note: The value of PubSubDomain affects the behavior of this method.
|
||||
@@ -1272,8 +1291,24 @@ namespace Spring.Messaging.Nms
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private class ExecuteSessionCallbackUsingDelegate : ISessionCallback
|
||||
{
|
||||
private readonly SessionDelegate del;
|
||||
public ExecuteSessionCallbackUsingDelegate(SessionDelegate del)
|
||||
{
|
||||
this.del = del;
|
||||
}
|
||||
|
||||
public object DoInNms(ISession session)
|
||||
{
|
||||
return del(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal class SimpleMessageCreator : IMessageCreator
|
||||
{
|
||||
private MessageTemplate jmsTemplate;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright <20> 2002-2006 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 Apache.NMS;
|
||||
|
||||
namespace Spring.Messaging.Nms
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback delegate for code that operates on a Session.
|
||||
/// </summary>
|
||||
/// <param name="session">The NMS ISession object.</param>
|
||||
/// <remarks>
|
||||
/// <para>Allows you to execute any number of operations
|
||||
/// on a single ISession, possibly returning a result a result.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <returns>A result object from working with the <code>Session</code>, if any (so can be <code>null</code>)
|
||||
/// </returns>
|
||||
/// <throws>NMSException if there is any problem </throws>
|
||||
/// <author>Mark Pollack</author>
|
||||
public delegate object SessionDelegate(ISession session);
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
using Spring.Util;
|
||||
using Apache.NMS;
|
||||
|
||||
namespace Spring.Messaging.Nms.Support.IDestinations
|
||||
namespace Spring.Messaging.Nms.Support.Destinations
|
||||
{
|
||||
/// <summary> Simple DestinationResolver implementation resolving destination names
|
||||
/// as dynamic destinations.</summary>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
using Apache.NMS;
|
||||
|
||||
namespace Spring.Messaging.Nms.Support.IDestinations
|
||||
namespace Spring.Messaging.Nms.Support.Destinations
|
||||
{
|
||||
/// <summary> Strategy interface for resolving NMS destinations.
|
||||
/// </summary>
|
||||
|
||||
@@ -23,7 +23,7 @@ using Spring.Objects.Factory;
|
||||
using Spring.Util;
|
||||
using Apache.NMS;
|
||||
|
||||
namespace Spring.Messaging.Nms.Support.IDestinations
|
||||
namespace Spring.Messaging.Nms.Support.Destinations
|
||||
{
|
||||
/// <summary> Base class for MessageTemplate} and other
|
||||
/// NMS-accessing gateway helpers, adding destination-related properties to
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Messaging\Nms\SessionDelegate.cs" />
|
||||
<Compile Include="Messaging\Nms\Config\MessageListenerContainerObjectDefinitionParser.cs" />
|
||||
<Compile Include="Messaging\Nms\Config\NmsNamespaceParser.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\CachedMessageProducer.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\CachedSession.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\CachingConnectionFactory.cs" />
|
||||
@@ -67,13 +70,14 @@
|
||||
<Compile Include="Messaging\Nms\ISessionCallback.cs" />
|
||||
<Compile Include="Messaging\Nms\Listener\AbstractListenerContainer.cs" />
|
||||
<Compile Include="Messaging\Nms\Listener\AbstractMessageListenerContainer.cs" />
|
||||
<Compile Include="Messaging\Nms\Listener\Adapter\ListenerExecutionFailedException.cs" />
|
||||
<Compile Include="Messaging\Nms\Listener\Adapter\MessageListenerAdapter.cs" />
|
||||
<Compile Include="Messaging\Nms\Listener\ISessionAwareMessageListener.cs" />
|
||||
<Compile Include="Messaging\Nms\Listener\LocallyExposedMessageResourceHolder.cs" />
|
||||
<Compile Include="Messaging\Nms\Listener\SimpleMessageListenerContainer.cs" />
|
||||
<Compile Include="Messaging\Nms\MessageCreatorDelegate.cs" />
|
||||
<Compile Include="Messaging\Nms\MessageGatewaySupport.cs" />
|
||||
<Compile Include="Messaging\Nms\MessageTemplate.cs" />
|
||||
<Compile Include="Messaging\Nms\MessagingGatewaySupport.cs" />
|
||||
<Compile Include="Messaging\Nms\Support\Converter\IMessageConverter.cs" />
|
||||
<Compile Include="Messaging\Nms\Support\Converter\MessageConversionException.cs" />
|
||||
<Compile Include="Messaging\Nms\Support\Converter\SimpleMessageConverter.cs" />
|
||||
@@ -97,6 +101,11 @@
|
||||
<Name>Spring.Data.2005</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Messaging\Nms\Config\spring-nms-1.2.xsd">
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -21,11 +21,16 @@
|
||||
<include name="**/*.cs" />
|
||||
<include name="../GenCommonAssemblyInfo.cs" />
|
||||
</sources>
|
||||
<references basedir="${current.bin.dir}">
|
||||
<include name="*.dll" />
|
||||
<exclude name="${project::get-name()}.dll" />
|
||||
<exclude name="CloverRuntime.dll" />
|
||||
</references>
|
||||
<resources prefix="Spring" dynamicprefix="true" failonempty="true">
|
||||
<include name="**/*.keys" />
|
||||
<include name="**/*.xsd" />
|
||||
<exclude name="**/obj/**" />
|
||||
</resources>
|
||||
<references basedir="${current.bin.dir}">
|
||||
<include name="*.dll" />
|
||||
<exclude name="${project::get-name()}.dll" />
|
||||
<exclude name="CloverRuntime.dll" />
|
||||
</references>
|
||||
</csc>
|
||||
</target>
|
||||
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright <20> 2002-2007 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
|
||||
|
||||
#region Imports
|
||||
|
||||
using System.Collections;
|
||||
using Apache.NMS;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using Spring.Context;
|
||||
using Spring.Context.Support;
|
||||
using Spring.Messaging.Nms.Connections;
|
||||
using Spring.Messaging.Nms.Listener;
|
||||
using Spring.Objects;
|
||||
using Spring.Objects.Factory.Xml;
|
||||
using Spring.Testing.NUnit;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Spring.Messaging.Nms.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// This class contains tests for
|
||||
/// </summary>
|
||||
/// <author>Mark Pollack</author>
|
||||
/// <version>$Id:$</version>
|
||||
[TestFixture]
|
||||
public class NmsNamespaceHandlerTests
|
||||
{
|
||||
|
||||
private static string DEFAULT_CONNECTION_FACTORY = "connectionFactory";
|
||||
|
||||
private static string EXPLICIT_CONNECTION_FACTORY = "testConnectionFactory";
|
||||
|
||||
|
||||
private IApplicationContext ctx;
|
||||
|
||||
private MockRepository mocks;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
NamespaceParserRegistry.RegisterParser(typeof(NmsNamespaceParser));
|
||||
ctx = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("NmsNamespaceHandlerTests.xml", GetType()));
|
||||
mocks = new MockRepository();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Registered()
|
||||
{
|
||||
Assert.IsNotNull(NamespaceParserRegistry.GetParser("http://www.springframework.net/nms"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ObjectsCreated()
|
||||
{
|
||||
IDictionary containers = ctx.GetObjectsOfType(typeof(SimpleMessageListenerContainer));
|
||||
Assert.AreEqual(3, containers.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainerConfiguration()
|
||||
{
|
||||
IDictionary containers = ctx.GetObjectsOfType(typeof (SimpleMessageListenerContainer));
|
||||
IConnectionFactory defaultConnectionFactory = (IConnectionFactory) ctx.GetObject(DEFAULT_CONNECTION_FACTORY);
|
||||
IConnectionFactory explicitConnectionFactory = (IConnectionFactory) ctx.GetObject(EXPLICIT_CONNECTION_FACTORY);
|
||||
|
||||
|
||||
int defaultConnectionFactoryCount = 0;
|
||||
int explicitConnectionFactoryCount = 0;
|
||||
foreach (DictionaryEntry dictionaryEntry in containers)
|
||||
{
|
||||
SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) dictionaryEntry.Value;
|
||||
if (container.ConnectionFactory.Equals(defaultConnectionFactory))
|
||||
{
|
||||
defaultConnectionFactoryCount++;
|
||||
}
|
||||
else if (container.ConnectionFactory.Equals(explicitConnectionFactory))
|
||||
{
|
||||
explicitConnectionFactoryCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, defaultConnectionFactoryCount, "1 container should have the default connectionFactory");
|
||||
Assert.AreEqual(2, explicitConnectionFactoryCount, "2 containers should have the explicit connectionFactory");
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Listeners()
|
||||
{
|
||||
TestObject testObject1 = (TestObject) ctx.GetObject("testObject1");
|
||||
TestObject testObject2 = (TestObject) ctx.GetObject("testObject2");
|
||||
TestMessageListener testObject3 = (TestMessageListener) ctx.GetObject("testObject3");
|
||||
|
||||
Assert.IsNull(testObject1.Name);
|
||||
Assert.IsNull(testObject2.Name);
|
||||
Assert.IsNull(testObject3.Message);
|
||||
|
||||
|
||||
ITextMessage message1 = (ITextMessage) mocks.CreateMock(typeof (ITextMessage));
|
||||
Expect.Call(message1.Text).Return("Test1");
|
||||
mocks.Replay(message1);
|
||||
|
||||
IMessageListener listener1 = GetListener("listener1");
|
||||
listener1.OnMessage(message1);
|
||||
Assert.AreEqual("Test1", testObject1.Name);
|
||||
mocks.Verify(message1);
|
||||
|
||||
|
||||
ITextMessage message2 = (ITextMessage)mocks.CreateMock(typeof(ITextMessage));
|
||||
Expect.Call(message2.Text).Return("Test1");
|
||||
mocks.Replay(message2);
|
||||
|
||||
IMessageListener listener2 = GetListener("listener2");
|
||||
listener2.OnMessage(message2);
|
||||
mocks.Verify(message2);
|
||||
|
||||
|
||||
ITextMessage message3 = (ITextMessage)mocks.CreateMock(typeof(ITextMessage));
|
||||
mocks.Replay(message3);
|
||||
|
||||
//Default naming strategy is to use full type name.
|
||||
IMessageListener listener3 = GetListener(typeof (SimpleMessageListenerContainer).FullName);
|
||||
listener3.OnMessage(message3);
|
||||
Assert.AreSame(message3, testObject3.Message);
|
||||
mocks.Verify(message3);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private IMessageListener GetListener(string containerObjectName)
|
||||
{
|
||||
SimpleMessageListenerContainer container =
|
||||
(SimpleMessageListenerContainer) ctx.GetObject(containerObjectName);
|
||||
return (IMessageListener) container.MessageListener;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<objects xmlns="http://www.springframework.net"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:nms="http://www.springframework.net/nms">
|
||||
|
||||
<nms:listener-container connection-factory="testConnectionFactory"
|
||||
destination-resolver="testDestinationResolver" message-converter="testMessageConverter">
|
||||
<nms:listener id="listener1" destination="testDestination" ref="testObject1" method="SetName"/>
|
||||
<nms:listener id="listener2" destination="testDestination" ref="testObject2" method="SetName"
|
||||
response-destination="responseDestination"/>
|
||||
</nms:listener-container>
|
||||
|
||||
<nms:listener-container>
|
||||
<nms:listener destination="testDestination" ref="testObject3"/>
|
||||
</nms:listener-container>
|
||||
|
||||
|
||||
<!-- the default ConnectionFactory -->
|
||||
<object id="connectionFactory" type="Spring.Messaging.Nms.Connections.TestConnectionFactory, Spring.Messaging.Nms.Tests"/>
|
||||
|
||||
<object id="testConnectionFactory" type="Spring.Messaging.Nms.Connections.TestConnectionFactory, Spring.Messaging.Nms.Tests"/>
|
||||
|
||||
|
||||
<!--
|
||||
<bean id="testActivationSpecFactory" class="org.springframework.jms.listener.endpoint.StubJmsActivationSpecFactory"/>
|
||||
-->
|
||||
|
||||
<object id="testDestinationResolver" type="Spring.Messaging.Nms.Support.Destinations.DynamicDestinationResolver, Spring.Messaging.Nms"/>
|
||||
|
||||
<object id="testMessageConverter" type="Spring.Messaging.Nms.Support.Converter.SimpleMessageConverter, Spring.Messaging.Nms"/>
|
||||
|
||||
|
||||
<object id="testObject1" type="Spring.Objects.TestObject, Spring.Core.Tests"/>
|
||||
|
||||
<object id="testObject2" type="Spring.Objects.TestObject, Spring.Core.Tests"/>
|
||||
|
||||
<object id="testObject3" type="Spring.Messaging.Nms.Connections.TestMessageListener, Spring.Messaging.Nms.Tests"/>
|
||||
|
||||
</objects>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
using Apache.NMS;
|
||||
|
||||
namespace Spring.Messaging.Nms.Connections
|
||||
{
|
||||
public class TestConnectionFactory : IConnectionFactory
|
||||
{
|
||||
#region IConnectionFactory Members
|
||||
|
||||
public IConnection CreateConnection()
|
||||
{
|
||||
return new TestConnection();
|
||||
}
|
||||
|
||||
public IConnection CreateConnection(string userName, string password)
|
||||
{
|
||||
return new TestConnection();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright 2002-2008 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 Apache.NMS;
|
||||
|
||||
namespace Spring.Messaging.Nms.Connections
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///
|
||||
/// </remarks>
|
||||
/// <author>Mark Pollack</author>
|
||||
public class TestMessageListener : IMessageListener
|
||||
{
|
||||
private IMessage message;
|
||||
|
||||
|
||||
public IMessage Message
|
||||
{
|
||||
get { return message; }
|
||||
set { message = value; }
|
||||
}
|
||||
|
||||
#region IMessageListener Members
|
||||
|
||||
public void OnMessage(IMessage message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,16 @@
|
||||
|
||||
using System;
|
||||
using Apache.NMS;
|
||||
using Apache.NMS.ActiveMQ;
|
||||
using Apache.NMS.ActiveMQ.Commands;
|
||||
using Rhino.Mocks;
|
||||
|
||||
namespace Spring.Messaging.Nms.Connections
|
||||
{
|
||||
|
||||
public class TestSession : ISession
|
||||
{
|
||||
private MockRepository mocks = new MockRepository();
|
||||
private int closeCount;
|
||||
private int createdCount;
|
||||
|
||||
@@ -63,7 +67,9 @@ namespace Spring.Messaging.Nms.Connections
|
||||
|
||||
public IMessageConsumer CreateConsumer(IDestination destination, string selector)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
//IConnectionFactory connectionFactory = (IConnectionFactory)mocks.CreateMock(typeof(IConnectionFactory));
|
||||
IMessageConsumer msgConsumer = (IMessageConsumer) mocks.CreateMock(typeof (IMessageConsumer));
|
||||
return msgConsumer;
|
||||
}
|
||||
|
||||
public IMessageConsumer CreateConsumer(IDestination destination, string selector, bool noLocal)
|
||||
@@ -78,7 +84,7 @@ namespace Spring.Messaging.Nms.Connections
|
||||
|
||||
public IQueue GetQueue(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return new ActiveMQQueue(name);
|
||||
}
|
||||
|
||||
public ITopic GetTopic(string name)
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright <20> 2002-2007 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
|
||||
|
||||
#region Imports
|
||||
|
||||
using System.Collections;
|
||||
using Apache.NMS;
|
||||
using NUnit.Framework;
|
||||
using Rhino.Mocks;
|
||||
using Spring.Messaging.Nms.Connections;
|
||||
using Spring.Messaging.Nms.Support.Destinations;
|
||||
using Spring.Transaction;
|
||||
using Spring.Transaction.Support;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Spring.Messaging.Nms
|
||||
{
|
||||
/// <summary>
|
||||
/// This class contains tests for
|
||||
/// </summary>
|
||||
/// <author>Mark Pollack</author>
|
||||
/// <version>$Id:$</version>
|
||||
[TestFixture]
|
||||
public class MessageTemplateTests
|
||||
{
|
||||
private MockRepository mocks;
|
||||
private IDestinationResolver mockDestinationResolver;
|
||||
private IConnectionFactory mockConnectionFactory;
|
||||
private IConnection mockConnection;
|
||||
|
||||
private ISession mockSession;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
mocks = new MockRepository();
|
||||
CreateMocks();
|
||||
}
|
||||
|
||||
private MessageTemplate CreateTemplate()
|
||||
{
|
||||
MessageTemplate template = new MessageTemplate();
|
||||
template.DestinationResolver = mockDestinationResolver;
|
||||
template.SessionTransacted = UseTransactedTemplate;
|
||||
return template;
|
||||
}
|
||||
|
||||
protected virtual bool UseTransactedSession
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
protected virtual bool UseTransactedTemplate
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
private void CreateMocks()
|
||||
{
|
||||
mockConnectionFactory = (IConnectionFactory) mocks.CreateMock(typeof (IConnectionFactory));
|
||||
mockConnection = (IConnection) mocks.CreateMock(typeof (IConnection));
|
||||
mockSession = (ISession) mocks.CreateMock(typeof (ISession));
|
||||
|
||||
IQueue queue = (IQueue) mocks.CreateMock(typeof (IQueue));
|
||||
|
||||
Expect.Call(mockConnectionFactory.CreateConnection()).Return(mockConnection).Repeat.Once();
|
||||
if (UseTransactedTemplate)
|
||||
{
|
||||
Expect.Call(mockConnection.CreateSession(AcknowledgementMode.Transactional)).Return(mockSession).Repeat.
|
||||
Once();
|
||||
}
|
||||
else
|
||||
{
|
||||
Expect.Call(mockConnection.CreateSession(AcknowledgementMode.AutoAcknowledge)).Return(mockSession).
|
||||
Repeat.
|
||||
Once();
|
||||
}
|
||||
Expect.Call(mockSession.Transacted).Return(true);
|
||||
|
||||
mockDestinationResolver =
|
||||
(IDestinationResolver) mocks.CreateMock(typeof (IDestinationResolver));
|
||||
mockDestinationResolver.ResolveDestinationName(mockSession, "testDestination", false);
|
||||
LastCall.Return(queue).Repeat.Any();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SessionCallback()
|
||||
{
|
||||
MessageTemplate template = CreateTemplate();
|
||||
template.ConnectionFactory = mockConnectionFactory;
|
||||
mockSession.Close();
|
||||
LastCall.On(mockSession).Repeat.Once();
|
||||
mockConnection.Close();
|
||||
LastCall.On(mockConnection).Repeat.Once();
|
||||
|
||||
mocks.ReplayAll();
|
||||
|
||||
template.Execute(delegate(ISession session)
|
||||
{
|
||||
bool b = session.Transacted;
|
||||
return null;
|
||||
});
|
||||
mocks.VerifyAll();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SessionCallbackWithinSynchronizedTransaction()
|
||||
{
|
||||
SingleConnectionFactory scf = new SingleConnectionFactory(mockConnectionFactory);
|
||||
MessageTemplate template = CreateTemplate();
|
||||
template.ConnectionFactory = scf;
|
||||
|
||||
mockConnection.Start();
|
||||
LastCall.On(mockConnection).Repeat.Times(2);
|
||||
Expect.Call(mockSession.Transacted).Return(UseTransactedSession).Repeat.Twice();
|
||||
|
||||
if (UseTransactedTemplate)
|
||||
{
|
||||
mockSession.Commit();
|
||||
LastCall.On(mockSession).Repeat.Once();
|
||||
}
|
||||
|
||||
mockSession.Close();
|
||||
LastCall.On(mockSession).Repeat.Once();
|
||||
mockConnection.Stop();
|
||||
LastCall.On(mockConnection).Repeat.Once();
|
||||
mockConnection.Close();
|
||||
LastCall.On(mockConnection).Repeat.Once();
|
||||
|
||||
mocks.ReplayAll();
|
||||
|
||||
|
||||
TransactionSynchronizationManager.InitSynchronization();
|
||||
|
||||
try
|
||||
{
|
||||
template.Execute(delegate(ISession session)
|
||||
{
|
||||
bool b = session.Transacted;
|
||||
return null;
|
||||
});
|
||||
template.Execute(delegate(ISession session)
|
||||
{
|
||||
bool b = session.Transacted;
|
||||
return null;
|
||||
});
|
||||
|
||||
Assert.AreSame(mockSession, ConnectionFactoryUtils.GetTransactionalSession(scf, null, false));
|
||||
Assert.AreSame(mockSession, ConnectionFactoryUtils.GetTransactionalSession(scf, scf.CreateConnection(), false));
|
||||
|
||||
//In Java this test was doing 'double-duty' and testing TransactionAwareConnectionFactoryProxy, which has
|
||||
//not been implemented in .NET
|
||||
|
||||
template.Execute(delegate(ISession session)
|
||||
{
|
||||
bool b = session.Transacted;
|
||||
return null;
|
||||
});
|
||||
|
||||
IList synchs = TransactionSynchronizationManager.Synchronizations;
|
||||
Assert.AreEqual(1, synchs.Count);
|
||||
ITransactionSynchronization synch = (ITransactionSynchronization)synchs[0];
|
||||
synch.BeforeCommit(false);
|
||||
synch.BeforeCompletion();
|
||||
synch.AfterCommit();
|
||||
synch.AfterCompletion(TransactionSynchronizationStatus.Unknown);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
TransactionSynchronizationManager.ClearSynchronization();
|
||||
//Assert.IsTrue(TransactionSynchronizationManager.ResourceDictionary.Count == 0);
|
||||
//Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive);
|
||||
scf.Dispose();
|
||||
}
|
||||
Assert.IsTrue(TransactionSynchronizationManager.ResourceDictionary.Count == 0);
|
||||
mocks.VerifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,19 +88,24 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Messaging\Nms\Config\NmsNamespaceHandlerTests.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\CachingConnectionFactoryTests.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\NmsTransactionManagerTests.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\MessageTransactionManagerTests.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\SingleConnectionFactoryTests.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\TestConnection.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\TestExceptionListener.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\TestMessageListener.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\TestMessageProducer.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\TestSession.cs" />
|
||||
<Compile Include="Messaging\Nms\Integration\LoggingExceptionHandler.cs" />
|
||||
<Compile Include="Messaging\Nms\Integration\SimpleMessageListener.cs" />
|
||||
<Compile Include="Messaging\Nms\Integration\SimpleMessageListenerContainerTests.cs" />
|
||||
<Compile Include="Messaging\Nms\Connections\TestConnectionFactory.cs" />
|
||||
<Compile Include="Messaging\Nms\MessageTemplateTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Messaging\Nms\Integration\SimpleMessageListenerContainerTests.xml" />
|
||||
<EmbeddedResource Include="Messaging\Nms\Config\NmsNamespaceHandlerTests.xml" />
|
||||
<Content Include="Spring.Messaging.Nms.Tests.dll.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
||||
Reference in New Issue
Block a user