diff --git a/Spring.build b/Spring.build index 5e437ff3..55fb31c0 100644 --- a/Spring.build +++ b/Spring.build @@ -1051,6 +1051,9 @@ + + diff --git a/doc/reference/src/messaging.xml b/doc/reference/src/messaging.xml index 5f333b6e..8274c2fd 100644 --- a/doc/reference/src/messaging.xml +++ b/doc/reference/src/messaging.xml @@ -9,118 +9,174 @@ when writing an enterprise strength messaging middleware applications. Spring achieves these goals in several ways. First it provides several helper classes that remove from the developer the incidental complexity - and resource management issues that arrise when using messaging APIs. In - particular, this chapter deals with messaging providers whose API is done - in the spirit of the Java Message Service (JMS) API. Vendors who provide a - JMS inspired API include TIBCO, IBM, and Sonic Software. (If you are using - Microsoft's Message Queue, please refer to the specific MSMQ section). Second, the design of these messaging - helper classes promote best practices in desigining a messaging - applicaiton by promoting a clear separation between the messaging - middleware specific code and business processing that is technology - agnostic. This is generally referred to a "plain old .NET object" (or - PONO) programming model. Lastly, as there is no defacto-standard common - API across messaging vendors, Spring provides an implementation for each - of the major messaging middleware vendors. Portability across each vendor - is promoted by providing a configuration schema that hides the actual - class types used for Spring's helper class as well as consistent naming of - the helper classes in different namespaces. The goal with vendor - portability is to get as far as possible by simply changing your 'using' - statements in code and schema name in the configuration. + and resource management issues that arise when using messaging APIs. + Second, the design of these messaging helper classes promote best + practices in designing a messaging application by promoting a clear + separation between the messaging middleware specific code and business + processing that is technology agnostic. This is generally referred to a + "plain old .NET object" (or PONO) programming model. + + This chapter discusses Spring's messaging support for providers + whose API was modeled after the Java Message Service (JMS) API. Vendors + who provide a JMS inspired API include Apache, TIBCO, IBM, and Progress + Software. If you are using Microsoft's Message Queue, please refer to the + specific MSMQ section. + + As there is no de facto-standard common API across messaging + vendors, Spring provides an implementation of its helper classes for each + of the major messaging middleware vendors. The naming of the classes you + will interact with most frequently will either be identical for each + provider, but located in a different namespace, or have their prefix + change to be the three-letter-acronym commonly associated with the message + provider. The list of providers supported by Spring is show below along + with their namespace and prefix. + + + + Apache NMS in namespace Spring.Messaging.Nms. + 'Nms' is sometimes used as the class prefix + + + + TIBCO EMS in namespace Spring.Messaging.Ems. + 'Ems' is sometimes used as the class prefix + + + + SonicMQ in namespace Spring.Messaging.Sonic, + 'Jms' is sometimes used as the class prefix. + + + + Websphere MQ in namespace + Spring.Messaging.Xms, 'Xms' is sometimes used as + the class prefix + + JMS can be roughly divided into two areas of functionality, namely - the production and consumption of messages. The - MessageTemplate class is used for message - production and synchronous message reception. For asynchronous reception, - Spring provides a multi-threaded message listener container, - SimpleMessageListenerContainer, that can be sued to - to create Message-Driven PONOs (MDPs). The MessageConverter interface is - used by both the MessageTemplate class and the message listener container - to converte between provider message types and PONOs. + the production and consumption of messages. For message production and the + synchronous consumption of messages the a template class, named + NmsTemplate, EmsTemplate (etc.) is + used. Asynchronous message consumption is performed though a + multi-threaded message listener container, + SimpleMessageListenerContainer. This message + listener container is used to create Message-Driven PONOs (MDPs) which + refer to a messaging callback class that consists of just 'plain .NET + object's and is devoid of any specific messaging types or other artifacts. + The IMessageConverter interface is used by both the + template class and the message listener container to convert between + provider message types and PONOs. - For each vendor there is a namespace of the type, - Spring.Messaging.<VendorAcronym>.Core. For - example, in the case of ActiveMQ, you would use - Spring.Messaging.Nms.Core, for TIBCO you would use - Spring.Messaging.Ems.Core. This namespace provides the - core functionality for messaging. It contains the - MessageTemplate class that simplifies the use of the - messaging APIs by handling the creation and release of resources, much - like the AdoTemplate does for ADO.NET. The design principle common to - Spring template classes is to provide helper methods to perform common - operations and for more sophisticated usage, delegate the essence of the - processing task to user implemented callback interfaces. The messaging - template follows the same design. The classes offer various convenience - methods for the sending of messages, consuming a message synchronously, - and exposing the message Session and MessageProducer to the user. + The namespace + Spring.Messaging.<Vendor>.Core contains the + messing template class (e.g. NmsTemplate). The template + class simplifies the use of the messaging APIs by handling the creation + and release of resources, much like the AdoTemplate + does for ADO.NET. The JMS inspired APIs are low-level API, much like + ADO.NET. As such, even the simplest of operations requires 10s of lines of + code with the bulk of that code related to resource management of + intermediate API objects Spring's messaging support, both in Java and + .NET, addresses the error-prone boiler plate coding style one needs when + using these APIs. + + The design principle common to Spring template classes is to provide + helper methods to perform common operations and for more sophisticated + usage, delegate the essence of the processing task to user implemented + callback interfaces. The messaging template follows the same design. The + message template class offer various convenience methods for the sending + of messages, consuming a message synchronously, and exposing the message + Session and MessageProducer to the user. The namespace Spring.Messaging.<VendorAcronym>.Support.Converter - provides a MessageConverter abstraction to convert + provides a IMessageConverter abstraction to convert between .NET objects and messages. The namespace Spring.Messaging.<VendorAcronym>.Support.Destinations provides various strategies for managing destinations, such as providing a - service locator for destinations stored in a directory service. + service locater for destinations stored in a directory service. Finally, the namespace Spring.Messaging.<VendorAcronym>.Connections provides an implementations of the ConnectionFactory suitable for use in - standalone applications. + standalone applications. - This chapter starts with a 'Quick tour for the impatient' that shows - you how to get up and running quickly using Spring's message helper - classes. You can also refer to the sample application that ships with - Spring for additional hands-on usage. The rest of the sections in this - chapter discusses each of the major helper classes in detail. + The rest of the sections in this chapter discusses each of the major + helper classes in detail. Please refer to the sample application that + ships with Spring for additional hands-on usage. + + + To simplify documenting features that are common across all + provider implementations of Spring's helper classes a specific provider, + Apache ActiveMQ, was selected. As such when you see 'NmsTemplate' in the + documentation, it also refers to EmsTemplate, XmsTemplate, etc. unless + specifically documented otherwise. The provider specific API classes are + typically named after their JMS counterparts with the possible exception + of a leading 'I' in front of interfaces in order to follow .NET naming + conventions. In the documentation these API artifacts are referred to as + 'ConnectionFactory', 'Session', 'Message', etc. without the leading + 'I'. +
Separation of Concerns The use of MessageConverters and a PONO programming model promote messaging best practices by applying the principal of Separation of - Concerns to messaging based architectures. The infrasructure concern of + Concerns to messaging based architectures. The infrastructure concern of publishing and consuming messages is separated from the concern of business processing. These two concerns are reflected in the architecture as two distinct layers, a message processing layer and a business processing layer. The benefit of this approach is that your - business processing is decoupled from the technology, making it more - likely to survive technological changes over time. Spring's - MessageConverters provide first class support for mapping messaging data - types to PONOs. Aside from being the link between the two layers, having - a pluggable strategy for message conversion helps support a loosely - coupled architecture over time. Message formats will change over time, - typically by the addition of new fields. MessageConverters can detect - different versions of messages and perform the appropriate mapping logic - to PONOs such so that multiple versions of a message can be supported - simultaneously, a common requirement in enterprise messaging - architectures. In can loosely associate Spring's MessageConverters to - XML/Object mappers but with a messaging twist. + business processing is decoupled from the messaging technology, making + it more likely to survive technological changes over time and also + easier to test. Spring's MessageConverters provides support for mapping + messaging data types to PONOs. Aside from being the link between the two + layers, MessageConverters provide a pluggable strategy to help support + the evolution of a loosely coupled architecture over time. Message + formats will change over time, typically by the addition of new fields. + MessageConverters can be implemented to detect different versions of + messages and perform the appropriate mapping logic to PONOs such so that + multiple versions of a message can be supported simultaneously, a common + requirement in enterprise messaging architectures.
- Interopability + Interoperability and provider portability - Messaging is a traditional area of interopabiltity across - heterogenous systems with messaging vendors providing support on + Messaging is a traditional area of Interoperability across + heterogeneous systems with messaging vendors providing support on multiple operating systems (Windows, UNIX, Mainframes OS's) as well as multiple language bindings (C, C++, Java, .NET, Perl, etc.). In 199x the - Java Community Process came up with a specifcation to provide a common - API across messaing providers as well as define some common messaging + Java Community Process came up with a specification to provide a common + API across messaging providers as well as define some common messaging functionality. This specification is know as the Java Message Service. From the API perspective, it can roughly be thought of as the messaging counterpart to the ADO.NET or JDBC APIs that provide portability across - different database providers. Given this history, when messaging vendors - created their .NET APIs, many did so by creating their own JMS inspired - API in .NET. The NMS project's goal is to provide a common API for .NET - thereby giving portability to the various .NET messaging providers. One - downside of the NMS API is that it is a low-level API, much like ADO.NET - and JDBC. Even the simplist of operations requires 10s of lines of code - with the bulk of that code related to resource management of - intermediate API objects. Note that the 'core' of the JMS/NMS API is - much simplier than with ADO.NET/JDBC. So while NMS provides portability - it also brings with it this API 'noise'. Spring's messaging support, - both in Java and .NET, addresses the error-prone boiler plate coding - style one needs when using thtese APIs. + different database providers. + + Given this history, when messaging vendors created their .NET + APIs, many did so by creating their own JMS inspired API in .NET. There + is no de facto-standard common API across messaging vendors. As such, + portability across vendors using Spring's helper classes is done by + changing the configuration schema in your configuration to a particular + vendor and doing a 'search-and-replace' on the code base, changing the + namespace and a few class names. While not ideal ,using Spring will push + you in the direction of isolating the messaging specific classes in its + own layer and therefore will reduce the impact of the changes you make + to the code when switch providers. You business logic classes called + into via Spring's messaging infrastructure will remain the same. + + The NMS project from Apache addresses the lack of a common API + across .NET messaging providers by providing an abstract interface based + API for messaging and several implementations for different providers. + At the time of this writing, the project is close to releasing a 1.0 + version that supports ApacheMQ, MSMQ, and TIBCO EMS. There are a few + outstanding issues at the moment that prevent one using NMS as a common + API for all messaging providers but hopefully these issues will be + resolved. Note, that NMS serves 'double' duty as the preferred API for + messaging with ActiveMQ as well as a providing portability across + different messaging providers.
@@ -130,168 +186,198 @@ oriented middleware. Not surprisingly, a Microsoft Message Queuing (MSMQ) binding is provided as part of WCF. The WCF programming model is higher level than the traditional messaging APIs such as JMS and NMS - since you are programing to a sevice interface and use metadata (either - XML or attributes) to configure the messaging behavior. This is a big - improvement over using low-level vendor specific APIs. However, at the - time of this writing, it is not clear that other messaging providers - will provide WCF bindings. A Spring Extensions project, Spring-NMS, - provides a WCF binding for NMS. This will let you use the WCF - programming model but still retain portability across messaging - providers. + since you are programing to a service interface and use metadata (either + XML or attributes) to configure the messaging behavior. If you prefer to + use this service-oriented, RPC style approach, to messaging middleware + then look to see if a vendor provides a WCF binding for your messaging + provider. Note that even with the option of using WCF, many people + prefer to sit 'closer to the metal' when using messaging middleware, to + access specific features and functionality not available in WCF, or + simply because they are more comfortable with that programming + model.
- Using Spring JMS + Using Spring Messaging
- JmsTemplate + Messaging Template overview - Code that uses the JmsTemplate only needs to implement callback - interfaces giving them a clearly defined contract. The IMessageCreator - callback interface creates a message given a Session provided by the - calling code in JmsTemplate. In order to allow for more complex usage of - the JMS API, the callback ISessionCallback provides the user with the - JMS session and the callback IProducerCallback exposes a Session and - MessageProducer pair. + Code that uses the messaging template classes + (NmsTemplate, EmsTemplate, + etc) only needs to implement callback interfaces giving them a clearly + defined contract. The IMessageCreator callback + interface creates a message given a Session provided by the calling code + in NmsTemplate. In order to allow for more complex + usage of the provider messaging API, the callback + ISessionCallback provides the user with the + provider specific messaging Session and the callback + IProducerCallback exposes a provider specific + Session and MessageProducer pair. - The JMS API exposes two types of send methods, one that takes - delivery mode, priority, and time-to-live as quality of service (QOS) - parameters and one that takes no QOS parameters which uses default - values. Since there are many send methods in JmsTemplate, the setting of - the QOS parameters have been exposed as bean properties to avoid - duplication in the number of send methods. Similarly, the timeout value - for synchronous receive calls is set using the property - ReceiveTimeout. + Provider messaging APIs typically expose two types of send + methods, one that takes delivery mode, priority, and time-to-live as + quality of service (QOS) parameters and one that takes no QOS parameters + which uses default values. Since there are many higher level send + methods in NmsTemplate, the setting of the QOS + parameters have been exposed as properties on the template class to + avoid duplication in the number of send methods. Similarly, the timeout + value for synchronous receive calls is set using the property + ReceiveTimeout. + + + Instances of the NmsTemplate class are + thread-safe once configured. This is important because it means that + you can configure a single instance of a + NmsTemplate and then safely inject this shared + reference into multiple collaborators. To be clear, the + NmsTemplate is stateful, in that it maintains a + reference to a ConnectionFactory, but this + state is not conversational state. +
Connections - The JmsTemplate requires a reference to a ConnectionFactory. The - ConnectionFactory is part of the JMS specification and serves as the - entry point for working with JMS. It is used by the client application - as a factory to create connections with the JMS provider and - encapsulates various configuration parameters, many of which are vendor - specific such as SSL configuration options. - - Note: The TIBCO implementation is not interface based and its - methods are not virtual so no additional functionality that may - otherewise be part of a ConnectionFactory 'Wrapper' are provided. This - type of functionality wil be available when Spring.NET uses the - implementation neutral NMS AP(s. + The NmsTemplate requires a reference to a + ConnectionFactory. The ConnectionFactory serves as the entry point for + working with the provider's messaging API. It is used by the client + application as a factory to create connections to the messaging server + and encapsulates various configuration parameters, many of which are + vendor specific such as SSL configuration options. + The TIBCO EMS ConnectionFactory is not interface based and its + methods are not virtual so no additional functionality that may + otherwise be part of a ConnectionFactory 'Wrappers' (to be discussed + later) are provided. +
Destination Management In Java implementations of JMS, Connections and Destinations are - 'administered objects' accessible though JNDI. In .NET each vendor has - selected a different approach, generally JNDI inspired, to retrieve - Connections and Destinations that were configured administratively. You - can use these vendor specific APIs to perform dependency injection on - references to JMS Destination objects in Sprng's XML configuration file - by creating am implementation of IObjectFactory. + 'administered objects' accessible though JNDI - a directory service much + like ActiveDirectory. In .NET each vendor has selected a different + approach to destination management. Some are JNDI inspired, allowing you + to retrieve Connections and Destinations that were configured + administratively. You can use these vendor specific APIs to perform + dependency injection on references to JMS Destination objects in + Spring's XML configuration file by creating am implementation of + IObjectFactory or alternatively configuring the + specific concrete class implementation for a messaging provider. - However, this approach of administerd objects can be quite + However, this approach of administered objects can be quite cumbersome if there are a large number of destinations in the application or if there are advanced destination management features - unique to the JMS provider. Examples of such advanced destination + unique to the messaging provider. Examples of such advanced destination management would be the creation of dynamic destinations or support for - a hierarchical namespace of destinations. The JmsTemplate delegates the - resolution of a destination name to a JMS destination object to an - implementation of the interface IDestinationResolver. - DynamicDestinationResolver is the default implementation used by - JmsTemplate and accommodates resolving dynamic destinations. A - JndiDestinationResolver is also provided that acts as a service locator - for destinations contained in JNDI and optionally falls back to the - behavior contained in DynamicDestinationResolver. + a hierarchical namespace of destinations. The + NmsTemplate delegates the resolution of a + destination name to a destination object by delegating to an + implementation of the interface + IDestinationResolver. + DynamicDestinationResolver is the default + implementation used by NmsTemplate and + accommodates resolving dynamic destinations. - Quite often the destinations used in a JMS application are only - known at runtime and therefore cannot be administratively created when - the application is deployed. This is often because there is shared + Quite often the destinations used in a messaging application are + only known at runtime and therefore cannot be administratively created + when the application is deployed. This is often because there is shared application logic between interacting system components that create destinations at runtime according to a well-known naming convention. - Even though the creation of dynamic destinations are not part of the JMS - specification, most vendors have provided this functionality. Dynamic - destinations are created with a name defined by the user which - differentiates them from temporary destinations and are often not - registered in a JNDI-like directory.. The API used to create dynamic - destinations varies from provider to provider since the properties - associated with the destination are vendor specific. However, a simple - implementation choice that is sometimes made by vendors is to disregard - the warnings in the JMS specification and to use the TopicSession method - createTopic(String topicName) or the QueueSession method - createQueue(String queueName) to create a new destination with default + Even though the creation of dynamic destinations are not part of the + original JMS specification, most vendors have provided this + functionality. Dynamic destinations are created with a name defined by + the user which differentiates them from temporary destinations and are + often not registered in a directory service. The API used to create + dynamic destinations varies from provider to provider since the + properties associated with the destination are vendor specific. However, + a simple implementation choice that is sometimes made by vendors is to + use the TopicSession method + CreateTopic(string topicName) or the + QueueSession method CreateQueue(string + queueName) to create a new destination with default destination properties. Depending on the vendor implementation, - DynamicDestinationResolver may then also create a physical destination - instead of only resolving one. + DynamicDestinationResolver may then also create a + physical destination instead of only resolving one. - The boolean property PubSubDomain determines the behavior of - dynamic destination resolution via implementations of the - DestinationResolver interface. + The boolean property PubSubDomain is used to + configure the NmsTemplate with knowledge of what + messaging 'domain' is being used. By default the value of this property + is false, indicating that the point-to-point domain, Queues, will be + used. This property is infrequently used as the provider messaging APIs + are now largely agnostic as to which messaging 'domain' is used, + referring to 'Destinations' rather than 'Queues' or 'Topics'. However, + this property does influence the behavior of dynamic destination + resolution via implementations of the + IDestinationResolver interface. - You can also configure the JmsTemplate with a default destination - via the property defaultDestination. The default destination will be - used with send and receive operations that do not refer to a specific - destination. + You can also configure the NmsTemplate with a default destination + via the property DefaultDestination. The default + destination will be used with send and receive operations that do not + refer to a specific destination.
Message Listener Containers One of the most common uses of JMS is to concurrently process - messages delivered asynchronously. + messages delivered asynchronously. A message listener container is used + to receive messages from a message queue and drive the + IMessageListener that is injected into it. The + listener container is responsible for all threading of message reception + and dispatches into the listener for processing. A message listener + container is the intermediary between an Message-Driven PONO (MDP) and a + messaging provider, and takes care of registering to receive messages, + resource acquisition and release, exception conversion and suchlike. + This allows you as an application developer to write the (possibly + complex) business logic associated with receiving a message (and + possibly responding to it), and delegates boilerplate messaging + infrastructure concerns to the framework. A subclass of AbstractMessageListenerContainer is used to - receive messages from JMS and drive the Message-Driven POCOs (MDPs) that - are injected into it. The - AbstractMessageListenerContainer is responsible - for all threading of message reception and dispatch into the MDPs for - processing. A message listener container is the intermediary between an - MDP and a messaging provider, and takes care of registering to receive - messages, participating in transactions, resource acquisition and - release, exception conversion and suchlike. This allows you as an - application developer to write the (posssibly complex) business logic - associated with receiving a message (and possibly responding to it), and - delegates boilerplate JMS infrastructure concerns to the framework. - There are one subclasses of + receive messages from JMS and drive the Message-Driven PONOs (MDPs) that + are injected into it. There are one subclasses of AbstractMessageListenerContainer packaged with - Spring - SimpleMessageListenerContainer. - - SimpleMessageListenerContainer creates a fixed number of JMS + Spring - SimpleMessageListenerContainer. + Additional subclasses, in particular to participate in distributed + transactions (if the provider supports it), will be provided in future + releases. SimpleMessageListenerContainer creates a fixed number of JMS sessions at startup and uses them throughout the lifespan of the - container. This subclass doesn't allow for dynamic adaption to runtime - demands or participate in transactional reception of messages. - - Spring.Java provides two other subclasses, one to support - distributed transactions and the other to provide a dynamic session - management to optimize concurrent processing. Distributed transaction - support is not provided by .NET C# vendors (AFAIK) and neither is the - dynamic session management support which is based on the - ServerSessionPool SPI - an optional part of the JMS - specification. + container.
Transaction Management - TBD. This relates to integration with Spring's transaction - management features, the ability to have transacted JMS sessions is - supported. + Spring provides a NmsTransactionManager that + manages transactions for a single ConnectionFactory. This allows + messaging applications to leverage the managed transaction features of + Spring as described in . The + NmsTransactionManager performs local resource + transactions, binding a Connection/Session pair from the specified + ConnectionFactory to the thread. NmsTemplate + automatically detects such transactional resources and operates on them + accordingly. + + Using Spring's SingleConnectionFactory will + result in a shared Connection, with each transaction having its own + independent Session.
Sending a Message - The JmsTemplate contains three convenience + The NmsTemplate contains three convenience methods to send a message. The methods are listed below. - void Send(Destination destination, IMessageCreator + void Send(IDestination destination, IMessageCreator messageCreator) @@ -306,31 +392,32 @@ - Which differ in how the destination is specified. In first case the - JMS Destination object is specified directly. The second case specifies - the destination using a string that is then resolved to a JMS JMS - Destination object using the DestinationResolver - associated with the template. The last method sends the message to the - destination specified by JmsTemplates + The method differ in how the destination is specified. In first case + the JMS Destination object is specified directly. The second case + specifies the destination using a string that is then resolved to a + messaging Destination object using the + IDestinationResolver associated with the template. + The last method sends the message to the destination specified by + NmsTemplate''s DefaultDestination property. - All methods take as an argument an instance of IMessageCreator which - defines the API contract for you to create the JMS message. The interface - is show below + All methods take as an argument an instance of + IMessageCreator which defines the API contract for + you to create the JMS message. The interface is show below public interface IMessageCreator { - Message CreateMessage(Session session); -}Intermediate JMS Sessions and MessageProducers needed to - send the message are managed by JmsTemplate. The session passed in to the - method is never null. There is a similar set methods that use a delegate - instead of the interface, which can be convenientwhen writing small - implementaitons in .NET 2.0 using anonymous delegates. Larger, more - complex implementations of the method 'CreateMessage' are better suited to - an interface based implementation. + IMessage CreateMessage(ISession session); +}Intermediate Sessions and MessageProducers needed to send + the message are managed by NmsTemplate. The session + passed in to the method is never null. There is a similar set methods that + use a delegate instead of the interface, which can be convenient when + writing small implementation in .NET 2.0 using anonymous delegates. + Larger, more complex implementations of the method 'CreateMessage' are + better suited to an interface based implementation. - void SendWithDelegate(Destination destination, + void SendWithDelegate(IDestination destination, MessageCreatorDelegate messageCreatorDelegate) @@ -347,34 +434,33 @@ The declaration of the delegate is - public delegate Message MessageCreatorDelegate(Session session); + public delegate IMessage MessageCreatorDelegate(ISession session); - The following class shows how to use the API with an anonymous - delegate, making for very terse syntax and easy access to local variables. - A more realistic example would create the JmsTemplate via dependency - injection, allowing for easy configuration of the connection string. A - convenience class, JmsGatewaySupport, already contains a property of type - JmsTemplate for you to use in + The following class shows how to use the SendWithDelegate method + with an anonymous delegate to create a MapMessage from the supplied + Session object. The use of the anonymous delegate allows for very terse + syntax and easy access to local variables. The + NmsTemplate is constructed by passing a reference to a + ConnectionFactory. public class SimplePublisher { - private JmsTemplate template; + private NmsTemplate template; public SimplePublisher() { - template = new JmsTemplate(new ConnectionFactory("tcp://localhost:7222")); - template.PubSubDomain = true; + template = new NmsTemplate(new ConnectionFactory("tcp://localhost:61616")); } public void Publish(string ticker, double price) { - template.SendWithDelegate("APP.STOCK", - delegate(Session session) + template.SendWithDelegate("APP.STOCK.MARKETDATA", + delegate(ISession session) { - MapMessage message = session.CreateMapMessage(); - message.SetString("TICKER", ticker); - message.SetDouble("PRICE", price); - message.Priority = 2; + IMapMessage message = session.CreateMapMessage(); + message.Body.SetString("TICKER", ticker); + message.Body.SetDouble("PRICE", price); + message.NMSPriority = 2; return message; }); } @@ -383,27 +469,38 @@ + A zero argument constructor and ConnectionFactory property are also + provided. Alternatively consider deriving from Spring's + NmsGatewaySupport convenience base class which provides + a ConnectionFactory property that will instantiate a NmsTemplate instance + that is made available via the property NmsTemplate. +
Using MessageConverters In order to facilitate the sending of domain model objects, the - JmsTemplate has various send methods that take a + NmsTemplate has various send methods that take a .NET object as an argument for a message's data content. The overloaded - methods ConvertAndSend and ReceiveAndConvert in - JmsTemplate delegate the conversion process to an - instance of the MessageConverter + methods ConvertAndSend and + ReceiveAndConvert in + NmsTemplate delegate the conversion process to an + instance of the IMessageConverter interface. This interface defines a simple contract to convert between .NET objects and JMS messages. The default implementation SimpleMessageConverter supports conversion between String and TextMessage, byte[] and BytesMesssage, and System.Collections.IDictionary and MapMessage. By using the converter, you and your application code can focus on the business object that is - being sent or received via JMS and not be concerned with the details of - how it is represented as a JMS message. + being sent or received via messaging and not be concerned with the + details of how it is represented as a JMS message. - The family of ConvertAndSend messages are similar to that of the - Send method with the additional argument of type IMessagePostProcessor. - These methods are listed below. + The sample applications contains a XmlMessageConverter that + converts objects to an XML string and vice-versa for sending via a + TextMessage. + + The family of ConvertAndSend messages are + similar to that of the Send method with the additional argument of type + IMessagePostProcessor. These methods are listed below. @@ -436,20 +533,64 @@ - In the previous example the message priority was set inside the - callback. Generally speaking, converters should not be responsible for - setting Quality of Service parameters since they are not aware of the - context in which they are being called. The following code show this in - action. + The example below uses the default message converter to send a + Hashtable as a message to the destination "APP.STOCK". public void PublishUsingDict(string ticker, double price) { IDictionary marketData = new Hashtable(); marketData.Add("TICKER", ticker); marketData.Add("PRICE", price); - template.ConvertAndSend("APP.STOCK", marketData); -}A reflection based converter that can converter arbitrary - objects is available as a seperate project. + template.ConvertAndSend("APP.STOCK.MARKETDATA", marketData); +}To accommodate the setting of message's properties, headers, + and body that can not be generally encapsulated inside a converter + class, the IMessageConverterPostProcessor + interface gives you access to the message after it has been converted + but before it is sent. The example below demonstrates how to modify a + message header and a property after a Hashtable is converted to a + message using the IMessagePostProcessor. The methods + ConvertAndSendUsingDelegate allow for the use of + a delegate to perform message post processing. This family of methods is + listed below + + + + void ConvertAndSendWithDelegate(object message, + MessagePostProcessorDelegate postProcessor) + + + + void ConvertAndSendWithDelegate(IDestination + destination, object message, MessagePostProcessorDelegate + postProcessor) + + + + void ConvertAndSendWithDelegate(string + destinationName, object message, MessagePostProcessorDelegate + postProcessor) + + + + The declaration of the delegate is + + public delegate IMessage MessagePostProcessorDelegate(IMessage message); + + The following code shows this in action. + + public void PublishUsingDict(string ticker, double price) +{ + IDictionary marketData = new Hashtable(); + marketData.Add("TICKER", ticker); + marketData.Add("PRICE", price); + template.ConvertAndSendWithDelegate("APP.STOCK.MARKETDATA", marketData, + delegate(IMessage message) + { + message.NMSPriority = 2; + message.NMSCorrelationID = new Guid().ToString(); + return message; + }); +}
@@ -458,9 +599,9 @@ While the send operations cover many common usage scenarios, there are cases when you want to perform multiple operations on a JMS Session or - MessageProducer. The SessionCallback and ProducerCallback expose the JMS + MessageProducer. The SessionCallback and ProducerCallback expose the Session and Session / MessageProducer pair respectfully. The Execute() - methods on JmsTemplate execute these callback methods. + methods on NmsTemplate execute these callback methods. @@ -468,10 +609,20 @@ action) + + public object Execute(ProducerDelegate + action) + + public object Execute(ISessionCallback action) + + + public object Execute(SessionDelegate + action) + Where ISessionCallback and IProducerCallback are @@ -485,6 +636,13 @@ { object DoInJms(Session session); } + + The delegate signatures are listed below and mirror the interface + method signature + + public delegate object SessionDelegate(ISession session); + +public delegate object ProducerDelegate(ISession session, IMessageProducer producer);
@@ -493,14 +651,16 @@
Synchronous Reception - While JMS is typically associated with asynchronous processing, it - is possible to consume messages synchronously. The overloaded - Receive(..) methods provide this functionality. During a + While messaging middleware is typically associated with + asynchronous processing, it is possible to consume messages + synchronously. The overloaded Receive(..) methods on + NmsTemplate provide this functionality. During a synchronous receive, the calling thread blocks until a message becomes available. This can be a dangerous operation since the calling thread can potentially be blocked indefinitely. The property - ReceiveTimeout specifies how long the - receiver should wait before giving up waiting for a message. + ReceiveTimeout on + NmsTemplate specifies how long the receiver + should wait before giving up waiting for a message. The Receive methods are listed below @@ -536,15 +696,18 @@ - The Recieve method without arguments used - the DefaultDestination. The - RecieveSelected methods apply the provided JMS + The Receive method without arguments will + use the DefaultDestination. The + ReceiveSelected methods apply the provided message selector string to the MessageConsumer that is created. The ReceiveAndConvert methods apply the - templates message converter when receiving a message. These methods are - listed below. + template's message converter when receiving a message. The message + converter to use is set using the property + MessageConverter and is the + SimpleMessageConverter implementation by default. + These methods are listed below. @@ -581,9 +744,10 @@
Asynchronous Reception - You can register a class that implements the - IMessageListener interface. In the case - of TIBCO EMS this interface is defined as + Asynchronous reception of messages occurs by the messaging + provider invoking a callback function. This is commonly an interface + such as the IMessageListener interface shown below, taken from the TIBCO + EMS provider. public interface IMessageListener { @@ -591,12 +755,55 @@ } Other vendors may provide a delegate based version of this - interface. + callback or even both a delegate and interface options. Apache ActiveMQ + supports only the use of delegates for message reception callbacks. As a + programming convenience in + Spring.Messaging.Nms.Core is an interface + IMessageListener that can be used with + NMS. + + Below is a simple implementation of the IMessageListener interface + that processing a message. + + using Spring.Messaging.Nms.Core; +using Apache.NMS; +using Common.Logging; + +namespace MyApp +{ + public class SimpleMessageListener : IMessageListener + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(SimpleMessageListener)); + + private int messageCount; + + public int MessageCount + { + get { return messageCount; } + } + + public void OnMessage(IMessage message) + { + messageCount++; + LOG.Debug("Message listener count = " + messageCount); + ITextMessage textMessage = message as ITextMessage; + if (textMessage != null) + { + LOG.Info("Message Text = " + textMessage.Text); + } else + { + LOG.Warn("Can not process message of type " message.GetType()); + } + } +} + + Once you've implemented your message listener, it's time to create + a message listener container. You register you listener with a message listener container that - specifies JMS configuration parameters and the number of concurrent - consumers to create. There is an abstract base class for message - listener containers, + specifies various messaging configuration parameters, such as the + ConnectionFactory, and the number of concurrent consumers to create. + There is an abstract base class for message listener containers, AbstractMessageListenerContainer, and one concrete implementation, SimpleMessageListenerContainer. @@ -605,141 +812,491 @@ ConcurrentConsumers. Here is a sample configuration - <object id="connectionFactory" type="TIBCO.EMS.ConnectionFactory, TIBCO.EMS"> - <constructor-arg index="0" value="tcp://localhost:7222"/> - </object> + + <object id="ConnectionFactory" type="Apache.NMS.ActiveMQ.ConnectionFactory, Apache.NMS.ActiveMQ"> + <constructor-arg index="0" value="tcp://localhost:61616"/> + </object> - <object id="messageListener" type="MyApp.MyMessageListener, MyApp"/> + <object id="MyMessageListener" type="MyApp.SimpleMessageListener, MyApp"/> - <object id="jmsContainer" type="Spring.Messaging.Tibco.Ems.Listener.SimpleMessageListenerContainer, Spring.Messaging.Tibco.Ems"> - <property name="ConnectionFactory" ref="connectionFactory"/> - <property name="DestinationName" value="APP.REQUEST"/> - <property name="ConcurrentConsumers" value="10"/> - <property name="MessageListener" ref="messageListener"/> - </object> + <object id="MessageListenerContainer" type="Spring.Messaging.Nms.Listener.SimpleMessageListenerContainer, Spring.Messaging.Nms"> + <property name="ConnectionFactory" ref="ConnectionFactory"/> + <property name="DestinationName" value="APP.REQUEST"/> + <property name="ConcurrentConsumers" value="10"/> + <property name="MessageListener" ref="MyMessageListener"/> + </object> + - The property PubSubDomain is by defalt false, - meaning point-to-point/Queue delivery semantics. The above configuration - will create 10 threads that process messages off of the queue named - "APP.REQUEST". The threads are those owned by the JMS provider as a - result of creating a JMS MessageConsumer. Other important properties are - ClientID, used to set the ClientID of the JMS - Connection and MessageSelector to specify the JMS + The above configuration will create 10 threads that process + messages off of the queue named "APP.REQUEST". The threads are those + owned by the messaging provider as a result of creating a + MessageConsumer. Other important properties are + ClientID, used to set the ClientID of the + Connection and MessageSelector to specify the 'sql-like' message selector string. Durable subscriptions are supported via the properties SubscriptionDurable and - DurableSubscriptionName. You may also register a - listener using the property ExceptionListener. + DurableSubscriptionName. You may also register an + exception listener using the property + ExceptionListener. -
- MessageListenerAdapater + A custom schema to create the + SimpleMessageListener container is also provided. + Using this schema the configuration above looks like the + following - The MessageListenerAdapter allows methods of a class that does - not implement the IMessageListener interface to be invoked upon - message delivery. Lets call this class the 'message handler' class. To - achive this goal the MessageListenerAdapter implements the standard - IMessageListener interface to recieve a message and then delegates the - processing to the message handler class. Since the message handler - class does not contain methods that refer to JMS artifacts such as - Message,TextMessage etc, the MessageListenerAdapter uses a - MessageConverter to bridge the JMS and 'plain object' worlds. As a - reminder, the provided SimpleMessageConverter converts from - TextMessage to string, BytesMessage to byte[], and MapMessage to - IDictionary. Once the incoming message is converted to an IDictionary - (for example) a method with the name 'Handle' is invoked via - reflection passing in the IDictionary as an argument. + <objects xmlns="http://www.springframework.net" + xmlns:nms="http://www.springframework.net/nms"> - Using the SimpleMessageConverter, your "plain old object' - messaging callback implementation would look like this. + <!-- other object definitions --> + <nms:listener-container connection-factory="ConnectionFactory" concurrency="10"> + <nms:listener ref="MyMessageListener" destination="APP.STOCK.REQUEST" /> + </nms:listener-container> - public class SimpleMessageHandler -{ - public void HandleObject(IDictionary dict) - { - ... - } +</objects> - public void HandleObject(string text) - { - ... - } + Exceptions that are thrown during message processing can be passed + to an implementation of IExceptionHandler and + registered with the container via the property + ExceptionListener. The registered + IExceptionHandler will be invoked if the + exception is of the type NMSException (or the + equivalent root exception type for other providers). The + SimpleMessageListenerContainer will logs the exception at error level + and not propagate the exception to the provider. All handling of + acknowledgement and/or transactions is done by the listener container. + You can override the method + HandleListenerException to change this + behavior. - public void HandleObject(byte[] data) - { - ... - } - + Please refer to the Spring SDK documentation for additional + description of the features and properties of + SimpleMessageListenerContainer. +
+ +
+ The ISessionAwareMessageListener interface + + The ISessionAwareMessageListener interface + is a Spring-specific interface that provides a similar contract to the + messaging provider's IMessageListener interface + or Listener delegate/event, but also provides the message handling + method with access to the Session from which the Message was + received. + + public interface ISessionAwareMessageListener +{ + void OnMessage(IMessage message, ISession session); } - Notice how the various message handling methods are strongly - typed according to the contents of the various Message types that they - can receive and handle. The following configuration shows how to hook - up this class to process incoming JMS messages. + You can also choose to implement this interface and register it + with the message listener container +
- - <object name="simpleMessageHandler, type="MyApp.SimpleMessageHandler, MyApp"/> - - <object name="simpleMessageConverter" - type="Spring.Messaging.Tibco.Ems.Support.Converter.SimpleMessageConverter, Spring.Messaging.Tibco.Ems"/> +
+ MessageListenerAdapater - <object id="messageListenerAdapter" type="Spring.Messaging.Tibco.Ems.Listener.Adapter.MessageListenerAdapter, "> - <property name="DelegateObject" ref="simpleMessageHandler"/> - <property name="DefaultListenerMethod" value="HandleObject"/> - <property name="MessageConverter" ref="simpleMessageConverter"/> - </object> + The MessageListenerAdapter class is the final component in + Spring's asynchronous messaging support: in a nutshell, it allows you to + expose almost any class to be invoked as a messaging callback (there are + of course some constraints). + Consider the following interface definition. Notice that although + the interface extends neither the + IMessageListener nor + ISessionAwareMessageListener interfaces, it can + still be used as a Message-Driven PONOs (MDP) via the use of the + MessageListenerAdapter class. Notice also how the + various message handling methods are strongly typed according to the + contents of the various Message types that they can receive and + handle. - <object id="connectionFactory" type="TIBCO.EMS.ConnectionFactory, TIBCO.EMS"> - <constructor-arg index="0" value="tcp://localhost:7222"/> - </object> + public interface MessageHandler { - <object id="jmsContainer" type="Spring.Messaging.Tibco.Ems.Listener.SimpleMessageListenerContainer, Spring.Messaging.Tibco.Ems"> - <property name="ConnectionFactory" ref="connectionFactory"/> - <property name="DestinationName" value="APP.REQUEST"/> - <property name="ConcurrentConsumers" value="10"/> - <property name="MessageListener" ref="messageListener"/> - </object> + void HandleMessage(string message); - Another of the capabilities of the MessageListenerAdapter class - is the ability to automatically send back a response Message if a - handler method returns a non-void value. Any non-null value that is - returned from the execution of the handler method will (in the default - configuration) be converted into a TextMessage. The resulting - TextMessage will then be sent to the Destination (if one exists) - defined in the JMS Reply-To property of the original Message, or the - default Destination set on the MessageListenerAdapter (if one has been - configured); if no Destination is found then an - InvalidDestinationException will be thrown (and please note that this - exception will not be swallowed and will propagate up the call - stack). + void HandleMessage(Hashtable message); - -
+ void HandleMessage(byte[] message); + +}
+ + and a class that implements this interface... + + public class DefaultMessageHandler : IMessageHandler { + // stub implementations elided for bevity... +} + + In particular, note how the above implementation of the + IMessageHandler interface (the above DefaultMessageHandler class) has no + messaging provider API dependencies at all. It truly is a PONO that we + will make into an MDP via the following configuration. + + <object id="MessagleHandler" type="MyApp.DefaultMessageHandler, MyApp"/> + +<object id="MessageListenerAdapter" type="Spring.Messaging.Nms.Listener.Adapter.MessageListenerAdapter, Spring.Messaging.Nms"> + <property name="HandlerObject" ref="MessagleHandler"/> +</object> + +<object id="MessageListenerContainer" type="Spring.Messaging.Nms.Listener.SimpleMessageListenerContainer, Spring.Messaging.Nms"> + <property name="ConnectionFactory" ref="ConnectionFactory"/> + <property name="DestinationName" value="APP.REQUEST"/> + <property name="MessageListener" ref="MessageListenerAdapter"/> +</object> + + The previous examples relies on the fact that the default + IMessageConverter implementation of the MessageListenerAdapter is + SimpleMessageConverter that can convert from messages to strings, + byte[], and hashtables and object from a ITextMessage, IBytesMessage, + IMapMessage, and IObjectMessage respectfully. + + Below is an example of another MDP that can only handle the + receiving of NMS ITextMessage messages. Notice how the message handling + method is actually called 'Receive' (the name of the message handling + method in a MessageListenerAdapter defaults to 'HandleMessage'), but it + is configurable (as you will see below). Notice also how the + 'Receive(..)' method is strongly typed to receive and respond only to + NMS ITextMessage messages. + + public interface TextMessageHandler { + + void Receive(ITextMessage message); +} + + public class TextMessageHandler implements ITextMessageHandler { + // implementation elided for clarity... +} + + The configuration of the attendant + MessageListenerAdapter would look like + this + + <object id="MessagleHandler" type="MyApp.DefaultMessageHandler, MyApp"/> + +<object id="MessageListenerAdapter" type="Spring.Messaging.Nms.Listener.Adapter.MessageListenerAdapter, Spring.Messaging.Nms"> + <property name="HandlerObject" ref="TextMessagleHandler"/> + <property name="DefaultHandlerMethod" value="Receive"/> + <!-- we don't want automatic message context extraction --> + <property name="MessageConverter"> + <null/> + </property> +</bean> + + Please note that if the above 'MessageListener' receives a Message + of a type other than ITextMessage, a + ListenerExecutionFailedException will be thrown + (and subsequently handled by the container by logging the + exception). + + If your IMessageConverter implementation + will return multiple object types, overloading the handler method is + perfectly acceptable, the most specific matching method will be used. A + method with an object signature would be consider a 'catch-all' method + of last resort. For example, you can have an handler interface as shown + below. + + public interface IMyHandler +{ + void DoWork(string text); + void DoWork(OrderRequest orderRequest); + void DoWork(InvoiceRequest invoiceRequest); + void DoWork(object obj); +} + + Another of the capabilities of the MessageListenerAdapter class is + the ability to automatically send back a response Message if a handler + method returns a non-void value. The adapter's message converter will be + used to convert the methods return value to a message. The resulting + message will then be sent to the Destination defined in the JMS Reply-To + property of the original Message (if one exists) , or the default + Destination set on the MessageListenerAdapter (if one has been + configured). If no Destination is found then an + InvalidDestinationException will be thrown (and + please note that this exception will not be swallowed and will propagate + up the call stack). + + An interface that is typical when used with a message converter + that supports multiple object types and has return values is shown + below. + + public interface IMyHandler +{ + string DoWork(string text); + OrderResponse DoWork(OrderRequest orderRequest); + InvoiceResponse DoWork(InvoiceRequest invoiceRequest); + void DoWork(object obj); +} + + +
+ +
+ Processing messages within a messaging transaction + + Invoking a message listener within a transaction only requires + reconfiguration of the listener container. Local message transactions + can be activated by setting the property SessionAcknowledgeMode which + for NMS is of the enum type AcknowledgementMode, to + AcknowledgementMode.Transactional. Each message listener invocation will + then operate within an active messaging transaction, with message + reception rolled back in case of listener execution failure. + + Sending a response message (via ISessionAwareMessageListener) will + be part of the same local transaction, but any other resource operations + (such as database access) will operate independently. This usually + requires duplicate message detection in the listener implementation, + covering the case where database processing has committed but message + processing failed to commit. See the discussion on the ActiveMQ web site + here for + more information combining local database and messaging + transactions. +
+ +
+ Messaging Namespace support + + To use the NMS namespace elements you will need to reference the + NMS schema. For information on how to set this up refer to . The namespace consists of one + top level elements: <listener-container/> which can contain one or + more <listener/> child elements. Here is an example of a basic + configuration for two listeners. + + <nms:listener-container> + + <nms:listener destination="queue.orders" ref="OrderService" method="PlaceOrder"/> + + <nms:listener destination="queue.confirmations" ref="ConfirmationLogger" method="Log"/> + +</nms:listener-container> + + The example above is equivalent to creating two distinct listener + container bean definitions and two distinct MessageListenerAdapter bean + definitions as demonstrated in the section entitled . In addition to the attributes + shown above, the listener element may contain several optional ones. The + following table describes all available attributes: + + + Attributes of the NMS <literal><listener></literal> + element + + + + + + + + + Attribute + + Description + + + + + + id + + A object name for the hosting listener container. + If not specified, a object name will be automatically + generated. + + + + destination (required) + + The destination name for this listener, resolved + through the IDestinationResolver + strategy. + + + + ref (required) + + The object name of the handler + object. + + + + method + + The name of the handler method to invoke. If the + ref points to a + IMessageListener or Spring + ISessionAwareMessageListener, + this attribute may be omitted. + + + + response-destination + + 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. + + + + subscription + + The name of the durable subscription, if + any. + + + + selector + + An optional message selector for this + listener. + + + +
+ + The <listener-container/> element also accepts several + optional attributes. This allows for customization of the various + strategies (for example, DestinationResolver) as well as basic messaging + settings and resource references. Using these attributes, it is possible + to define highly-customized listener containers while still benefiting + from the convenience of the namespace. + + <jms:listener-container connection-factory="MyConnectionFactory" + destination-resolver="MyDestinationResolver" + concurrency="10"> + + <jms:listener destination="queue.orders" ref="OrderService" method="PlaceOrder"/> + + <jms:listener destination="queue.confirmations" ref="ConfirmationLogger" method="Log"/> + +</jms:listener-container> + + The following table describes all available attributes. Consult + the class-level SDK documentation of the + AbstractMessageListenerContainer and its subclass + SimpleMessageListenerContainer for more detail on + the individual properties. + + + Attributes of the NMS + <literal><listener-container></literal> element + + + + + + + + + Attribute + + Description + + + + + + connection-factory + + A reference to the NMS + ConnectionFactory bean (the + default object name is + 'ConnectionFactory'). + + + + destination-resolver + + A reference to the + IDestinationResolver strategy for + resolving JMS + Destinations. + + + + message-converter + + A reference to the + IMessageConverter strategy for + converting NMS Messages to listener method arguments. Default is + a SimpleMessageConverter. + + + + destination-type + + The NMS destination type for this listener: + queue, topic or + durableTopic. The default is + queue. + + + + client-id + + The NMS client id for this listener container. + Needs to be specified when using durable + subscriptions. + + + + acknowledge + + The native NMS acknowledge mode: + auto, client, + dups-ok or transacted. A + value of transacted activates a locally + transacted Session. As an + alternative, specify the transaction-manager + attribute described below. Default is + auto. + + + + concurrency + + 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 queue ordering is + important; consider raising it for general + queues. + + + +
- TIBCO Specific Details + TIBCO EMS Specific Details - Caching of JMS resources is usually done by a wrapping the 'raw - provider' JMS implementation with an implementation that will cache JMS - resources. The resources that are candidates for caching are the JMS - Connection, Session, and MessageProducer. The JMS specification requires - that the Connection be thread safe. The Session and MessageProducer are - not required to be thread safe but they are in TIBCO's implementation. The - most important resource to cache is the JMS Connection since the flow of - events in JmsTemplate is to create/close a connection on each operation - and this is an expensive operation. In the Java version of JmsTemplate a - class, SingleConnectionFactory is provided in which the same Connection is - returned on calls to createConnection() and all calls to .close() on the - returned Connection are ignored. Since TIBCO's connection class does not - have an interface nor virtual methods this strategy is not possible. An - alternative strategy is to 'hard-code' the caching of these resources - within JmsTemplate and SimpleMessageContainer. This functionality is - controlled by the property CacheJmsResources and is - set to true by default, resulting in caching of Connection, Session, and - MessageProducer. When integration with NMS (a set of common interfaces for - .NET JMS providers) is completed this will not be necessary and we can use - a wrapper implementation of the NMS API that performs caching of JMS - resources in an appropriate manner for each vendor. + Caching of messaging resources is usually done by a wrapping the + 'raw provider' provider implementation with an implementation that will + cache messaging resources. The resources that are candidates for caching + are the Connection, Session, and MessageProducer. The JMS specification + requires that the Connection be thread safe. The Session and + MessageProducer are not required to be thread safe but they are in TIBCO's + implementation. The most important resource to cache is the JMS Connection + since the flow of events in the message template class (EmsTemplate) is to + create/close a connection on each operation and this is an expensive + operation. + + Spring provides a convenience class, SingleConnectionFactory, in + which the same Connection is returned on calls to CreateConnection() and + all calls to .Close() on the returned Connection are ignored. Since + TIBCO's Connection class does not have an interface nor virtual methods + this strategy is not possible. An alternative strategy used is to + 'hard-code' the caching of these resources within EmsTemplate and + SimpleMessageContainer. This functionality is controlled by the property + CacheJmsResources and is set to true by default, + resulting in caching of Connection, Session, and MessageProducer. When + using the TIBCO EMS binding for the NMS APIs you do not need to set this + property and can instead use one of the wrapper implementations in Spring + based on the NMS API that performs caching of messaging resources.
\ No newline at end of file diff --git a/doc/reference/src/xsd-configuration.xml b/doc/reference/src/xsd-configuration.xml index 498c6cce..ae35b933 100644 --- a/doc/reference/src/xsd-configuration.xml +++ b/doc/reference/src/xsd-configuration.xml @@ -255,6 +255,44 @@ </parsers> </spring> +</configuration> +
+ +
+ The <literal>nms</literal> messaging schema + + The nms tags are for use when you want to + configure Spring's messaging support. The tags are comprehensively + covered in the chapter + + <?xml version="1.0" encoding="UTF-8"?> +<objects xmlns="http://www.springframework.net" + xmlns:r="http://www.springframework.net/nms"> + + <!-- <object/> definitions here --> + + <!-- <nms/> remoting definitions here --> + +</objects> + + You will also need to configure the remoting namespace parser in + the main .NET application configuration file as shown below + + <configuration> + + <configSections> + <sectionGroup name="spring"> + <!-- other Spring config sections handler like context, typeAliases, etc not shown for brevity --> + <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/> + </sectionGroup> + </configSections> + + <spring> + <parsers> + <parser type="Spring.Messaging.Nms.Config.NmsNamespaceParser, Spring.Messaging.Nms" /> + </parsers> + </spring> + </configuration>
diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Config/spring-nms-1.2.xsd b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Config/spring-nms-1.2.xsd index 135f13f4..3aabc1d6 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Config/spring-nms-1.2.xsd +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Config/spring-nms-1.2.xsd @@ -40,11 +40,11 @@ - + diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsTransactionManager.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsTransactionManager.cs index 6a092744..4cb6c81f 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsTransactionManager.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsTransactionManager.cs @@ -75,7 +75,7 @@ namespace Spring.Messaging.Nms.Connections /// /// /// The ConnectionFactory has to be set before using the instance. - /// This constructor can be used to prepare a MessageTemplate via a ApplicationContext, + /// This constructor can be used to prepare a NmsTemplate via a ApplicationContext, /// typically setting the ConnectionFactory via ConnectionFactory property. /// /// Turns off transaction synchronization by default, as this manager might diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/IMessagePostProcessor.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/IMessagePostProcessor.cs index 6d9ee504..37494b7f 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/IMessagePostProcessor.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/IMessagePostProcessor.cs @@ -22,7 +22,7 @@ using Apache.NMS; namespace Spring.Messaging.Nms.Core { - /// To be used with MessageTemplate's send method that + /// To be used with NmsTemplate's send method that /// convert an object to a message. /// /// diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/INmsOperations.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/INmsOperations.cs index 228bb586..a853185e 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/INmsOperations.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/INmsOperations.cs @@ -25,10 +25,10 @@ namespace Spring.Messaging.Nms.Core /// Specifies a basic set of NMS operations. /// /// - ///

Implemented by MessageTemplate. Not often used but a useful option + ///

Implemented by NmsTemplate. Not often used but a useful option /// to enhance testability, as it can easily be mocked or stubbed.

/// - ///

Provides MessageTemplate's send(..) and + ///

Provides NmsTemplate's send(..) and /// receive(..) methods that mirror various NMS API methods. /// See the NMS specification and NMS API docs for details on those methods. ///

@@ -42,10 +42,6 @@ namespace Spring.Messaging.Nms.Core /// a NMS Session. ///
/// - /// 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.b - /// /// callback object that exposes the session /// /// the result object from working with the session @@ -57,10 +53,6 @@ namespace Spring.Messaging.Nms.Core /// a NMS Session. /// /// - /// 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.b - /// /// delegate that exposes the session /// the result object from working with the session /// @@ -71,13 +63,23 @@ namespace Spring.Messaging.Nms.Core /// the NMS session and MessageProducer in order to do more complex /// send operations. /// - /// callback object that exposes the session/producer pair + /// delegate that exposes the session/producer pair /// /// the result object from working with the session /// /// NMSException if there is any problem - object Execute(IProducerCallback action); - + object Execute(ProducerDelegate del); + + /// Send a message to a NMS destination. The callback gives access to + /// the NMS session and MessageProducer in order to do more complex + /// send operations. + /// + /// callback object that exposes the session/producer pair + /// + /// the result object from working with the session + /// + /// NMSException if there is any problem + object Execute(IProducerCallback action); //------------------------------------------------------------------------- // Convenience methods for sending messages @@ -122,7 +124,7 @@ namespace Spring.Messaging.Nms.Core /// delegate callback to create a message /// /// NMSException if there is any problem - void SendWithDelegate(IMessageCreatorDelegate messageCreatorDelegate); + void SendWithDelegate(MessageCreatorDelegate messageCreatorDelegate); /// Send a message to the specified destination. /// The IMessageCreator callback creates the message given a Session. @@ -132,7 +134,7 @@ namespace Spring.Messaging.Nms.Core /// delegate callback to create a message /// /// NMSException if there is any problem - void SendWithDelegate(IDestination destination, IMessageCreatorDelegate messageCreatorDelegate); + void SendWithDelegate(IDestination destination, MessageCreatorDelegate messageCreatorDelegate); /// Send a message to the specified destination. /// The IMessageCreator callback creates the message given a Session. @@ -143,7 +145,7 @@ namespace Spring.Messaging.Nms.Core /// delegate callback to create a message /// /// NMSException if there is any problem - void SendWithDelegate(string destinationName, IMessageCreatorDelegate messageCreatorDelegate); + void SendWithDelegate(string destinationName, MessageCreatorDelegate messageCreatorDelegate); //------------------------------------------------------------------------- // Convenience methods for sending auto-converted messages @@ -217,7 +219,40 @@ namespace Spring.Messaging.Nms.Core /// /// NMSException if there is any problem void ConvertAndSend(string destinationName, object message, IMessagePostProcessor postProcessor); - + + /// + /// Send the given object to the default destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + ///

This will only work with a default destination specified!

+ ///
+ /// the object to convert to a message + /// the callback to modify the message + /// NMSException if there is any problem + void ConvertAndSendWithDelegate(object message, MessagePostProcessorDelegate postProcessor); + + /// + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the destination to send this message to + /// the object to convert to a message + /// the callback to modify the message + /// NMSException if there is any problem + void ConvertAndSendWithDelegate(IDestination destination, object message, MessagePostProcessorDelegate postProcessor); + + /// + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// the object to convert to a message. + /// the callback to modify the message + /// NMSException if there is any problem + void ConvertAndSendWithDelegate(string destinationName, object message, MessagePostProcessorDelegate postProcessor); //------------------------------------------------------------------------- // Convenience methods for receiving messages diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/ISessionCallback.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/ISessionCallback.cs index daa7914c..6418ef91 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/ISessionCallback.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/ISessionCallback.cs @@ -26,7 +26,7 @@ namespace Spring.Messaging.Nms.Core /// Session ///
/// - /// To be used with the MessageTemplate.Execute(ISessionCallback)} + /// To be used with the NmsTemplate.Execute(ISessionCallback)} /// method. See for the equivalent callback /// that can be used as a (anonymous) delegate. /// diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/MessageCreatorDelegate.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/MessageCreatorDelegate.cs index a14fd38f..b3a35971 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/MessageCreatorDelegate.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/MessageCreatorDelegate.cs @@ -31,5 +31,5 @@ namespace Spring.Messaging.Nms.Core /// the Message to be sent /// /// NMSException if thrown by NMS API methods - public delegate IMessage IMessageCreatorDelegate(ISession session); + public delegate IMessage MessageCreatorDelegate(ISession session); } diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/MessagePostProcessorDelegate.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/MessagePostProcessorDelegate.cs new file mode 100644 index 00000000..33fd8e0d --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/MessagePostProcessorDelegate.cs @@ -0,0 +1,35 @@ +#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.Core +{ + + /// + /// Delegate that is used with NmsTemplate's ConvertAndSend method that converts + /// an object. + /// + /// It allows for further modification of the message after it has been processed + /// by the converter. This is useful for setting of NMS Header and Properties. + /// + /// Mark Pollack + public delegate IMessage MessagePostProcessorDelegate(IMessage message); +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/NmsGatewaySupport.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/NmsGatewaySupport.cs index 1c9141be..95fe961e 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/NmsGatewaySupport.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/NmsGatewaySupport.cs @@ -29,9 +29,9 @@ namespace Spring.Messaging.Nms.Core /// Convenient super class for application classes that need NMS access. ///
/// - /// Requires a ConnectionFactory or a MessageTemplate instance to be set. - /// It will create its own MessageTemplate if a ConnectionFactory is passed in. - /// A custom MessageTemplate instance can be created for a given ConnectionFactory + /// Requires a ConnectionFactory or a NmsTemplate instance to be set. + /// It will create its own NmsTemplate if a ConnectionFactory is passed in. + /// A custom NmsTemplate instance can be created for a given ConnectionFactory /// through overriding the createNmsTemplate method. /// /// @@ -59,7 +59,7 @@ namespace Spring.Messaging.Nms.Core /// /// Gets or sets he NMS connection factory to be used by the gateway. - /// Will automatically create a MessageTemplate for the given ConnectionFactory. + /// Will automatically create a NmsTemplate for the given ConnectionFactory. /// /// The connection factory. public IConnectionFactory ConnectionFactory @@ -75,10 +75,10 @@ namespace Spring.Messaging.Nms.Core } /// - /// Creates a MessageTemplate for the given ConnectionFactory. + /// Creates a NmsTemplate for the given ConnectionFactory. /// /// Only invoked if populating the gateway with a ConnectionFactory reference. - /// Can be overridden in subclasses to provide a different MessageTemplate instance + /// Can be overridden in subclasses to provide a different NmsTemplate instance /// /// /// The connection factory. diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/NmsTemplate.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/NmsTemplate.cs index 38a18ae1..87b4e3d3 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/NmsTemplate.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/NmsTemplate.cs @@ -63,7 +63,7 @@ namespace Spring.Messaging.Nms.Core /// public static readonly long DEFAULT_RECEIVE_TIMEOUT = -1; - private MessageTemplateResourceFactory transactionalResourceFactory; + private NmsTemplateResourceFactory transactionalResourceFactory; private object defaultDestination; @@ -90,20 +90,20 @@ namespace Spring.Messaging.Nms.Core #region Constructor (s) - /// Create a new MessageTemplate. + /// Create a new NmsTemplate. /// /// Note: The ConnectionFactory has to be set before using the instance. - /// This constructor can be used to prepare a MessageTemplate via an ObjectFactory, + /// This constructor can be used to prepare a NmsTemplate via an ObjectFactory, /// typically setting the ConnectionFactory. /// public NmsTemplate() { - transactionalResourceFactory = new MessageTemplateResourceFactory(this); + transactionalResourceFactory = new NmsTemplateResourceFactory(this); InitDefaultStrategies(); } - /// Create a new MessageTemplate, given a ConnectionFactory. + /// Create a new NmsTemplate, given a ConnectionFactory. /// the ConnectionFactory to obtain IConnections from /// public NmsTemplate(IConnectionFactory connectionFactory) @@ -130,7 +130,7 @@ namespace Spring.Messaging.Nms.Core if (defaultDestination == null) { throw new SystemException( - "No defaultDestination or defaultDestinationName specified. Check configuration of MessageTemplate."); + "No defaultDestination or defaultDestinationName specified. Check configuration of NmsTemplate."); } } @@ -139,7 +139,7 @@ namespace Spring.Messaging.Nms.Core { if (MessageConverter == null) { - throw new SystemException("No messageConverter registered. Check configuration of MessageTemplate."); + throw new SystemException("No messageConverter registered. Check configuration of NmsTemplate."); } } @@ -493,7 +493,7 @@ namespace Spring.Messaging.Nms.Core /// The session to operate on. /// The destination to send to. /// The message creator delegate callback to create a Message. - protected internal virtual void DoSend(ISession session, IDestination destination, IMessageCreatorDelegate messageCreatorDelegate) + protected internal virtual void DoSend(ISession session, IDestination destination, MessageCreatorDelegate messageCreatorDelegate) { AssertUtils.ArgumentNotNull(messageCreatorDelegate, "IMessageCreatorDelegate must not be null"); DoSend(session, destination, null, messageCreatorDelegate); @@ -522,7 +522,7 @@ namespace Spring.Messaging.Nms.Core /// /// NMSException if thrown by NMS API methods protected internal virtual void DoSend(ISession session, IDestination destination, IMessageCreator messageCreator, - IMessageCreatorDelegate messageCreatorDelegate) + MessageCreatorDelegate messageCreatorDelegate) { @@ -630,13 +630,27 @@ namespace Spring.Messaging.Nms.Core return Execute(new ProducerCreatorCallback(this, action)); } + /// Send a message to a NMS destination. The callback gives access to + /// the NMS session and MessageProducer in order to do more complex + /// send operations. + /// + /// delegate that exposes the session/producer pair + /// + /// the result object from working with the session + /// + /// NMSException if there is any problem + public object Execute(ProducerDelegate del) + { + return Execute(new ProducerCreatorCallback(this, del)); + } + /// Send a message to the default destination. ///

This will only work with a default destination specified!

///
/// delegate callback to create a message /// /// NMSException if there is any problem - public void SendWithDelegate(IMessageCreatorDelegate messageCreatorDelegate) + public void SendWithDelegate(MessageCreatorDelegate messageCreatorDelegate) { CheckDefaultDestination(); if (DefaultDestination != null) @@ -657,7 +671,7 @@ namespace Spring.Messaging.Nms.Core /// delegate callback to create a message /// /// NMSException if there is any problem - public void SendWithDelegate(IDestination destination, IMessageCreatorDelegate messageCreatorDelegate) + public void SendWithDelegate(IDestination destination, MessageCreatorDelegate messageCreatorDelegate) { Execute(new SendDestinationCallback(this, destination, messageCreatorDelegate), false); } @@ -671,7 +685,7 @@ namespace Spring.Messaging.Nms.Core /// delegate callback to create a message /// /// NMSException if there is any problem - public void SendWithDelegate(string destinationName, IMessageCreatorDelegate messageCreatorDelegate) + public void SendWithDelegate(string destinationName, MessageCreatorDelegate messageCreatorDelegate) { Execute(new SendDestinationCallback(this, destinationName, messageCreatorDelegate), false); } @@ -829,6 +843,64 @@ namespace Spring.Messaging.Nms.Core } + + /// + /// Send the given object to the default destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + ///

This will only work with a default destination specified!

+ ///
+ /// the object to convert to a message + /// the callback to modify the message + /// NMSException if there is any problem + public void ConvertAndSendWithDelegate(object message, MessagePostProcessorDelegate postProcessor) + { + //Execute(new SendDestinationCallback(this, destination, messageCreatorDelegate), false); + CheckDefaultDestination(); + if (DefaultDestination != null) + { + ConvertAndSendWithDelegate(DefaultDestination, message, postProcessor); + } + else + { + ConvertAndSendWithDelegate(DefaultDestinationName, message, postProcessor); + } + } + + /// + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the destination to send this message to + /// the object to convert to a message + /// the callback to modify the message + /// NMSException if there is any problem + public void ConvertAndSendWithDelegate(IDestination destination, object message, + MessagePostProcessorDelegate postProcessor) + { + CheckMessageConverter(); + Send(destination, new ConvertAndSendMessageCreator(this, message, postProcessor)); + } + + /// + /// Send the given object to the specified destination, converting the object + /// to a NMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// the object to convert to a message. + /// the callback to modify the message + /// NMSException if there is any problem + public void ConvertAndSendWithDelegate(string destinationName, object message, + MessagePostProcessorDelegate postProcessor) + { + CheckMessageConverter(); + Send(destinationName, new ConvertAndSendMessageCreator(this, message, postProcessor)); + + } + /// Receive a message synchronously from the default destination, but only /// wait up to a specified time for delivery. ///

This method should be used carefully, since it will block the thread @@ -1123,11 +1195,11 @@ namespace Spring.Messaging.Nms.Core ///

/// ResourceFactory implementation that delegates to this template's callback methods. /// - private class MessageTemplateResourceFactory : ConnectionFactoryUtils.ResourceFactory + private class NmsTemplateResourceFactory : ConnectionFactoryUtils.ResourceFactory { private NmsTemplate enclosingTemplateInstance; - public MessageTemplateResourceFactory(NmsTemplate enclosingInstance) + public NmsTemplateResourceFactory(NmsTemplate enclosingInstance) { InitBlock(enclosingInstance); } @@ -1172,6 +1244,7 @@ namespace Spring.Messaging.Nms.Core { private NmsTemplate jmsTemplate; private IProducerCallback producerCallback; + private ProducerDelegate producerDelegate; public ProducerCreatorCallback(NmsTemplate jmsTemplate, IProducerCallback producerCallback) { @@ -1179,12 +1252,25 @@ namespace Spring.Messaging.Nms.Core this.producerCallback = producerCallback; } + public ProducerCreatorCallback(NmsTemplate jmsTemplate, ProducerDelegate producerDelegate) + { + this.jmsTemplate = jmsTemplate; + this.producerDelegate = producerDelegate; + } + public object DoInNms(ISession session) { IMessageProducer producer = jmsTemplate.CreateProducer(session, null); try { - return producerCallback.DoInNms(session, producer); + if (producerCallback != null) + { + return producerCallback.DoInNms(session, producer); + } + else + { + return producerDelegate(session, producer); + } } finally { @@ -1234,6 +1320,7 @@ namespace Spring.Messaging.Nms.Core private NmsTemplate jmsTemplate; private object objectToConvert; private IMessagePostProcessor messagePostProcessor; + private MessagePostProcessorDelegate messagePostProcessorDelegate; public ConvertAndSendMessageCreator(NmsTemplate jmsTemplate, object message, IMessagePostProcessor messagePostProcessor) { @@ -1242,11 +1329,25 @@ namespace Spring.Messaging.Nms.Core this.messagePostProcessor = messagePostProcessor; } + public ConvertAndSendMessageCreator(NmsTemplate jmsTemplate, object message, MessagePostProcessorDelegate messagePostProcessorDelegate) + { + this.jmsTemplate = jmsTemplate; + objectToConvert = message; + this.messagePostProcessorDelegate = messagePostProcessorDelegate; + } + public IMessage CreateMessage(ISession session) { IMessage msg = jmsTemplate.MessageConverter.ToMessage(objectToConvert, session); - return messagePostProcessor.PostProcessMessage(msg); + if (messagePostProcessor != null) + { + return messagePostProcessor.PostProcessMessage(msg); + } else + { + return messagePostProcessorDelegate(msg); + } } + } private class ReceiveSelectedCallback : ISessionCallback @@ -1336,7 +1437,7 @@ namespace Spring.Messaging.Nms.Core private IDestination destination; private NmsTemplate jmsTemplate; private IMessageCreator messageCreator; - private IMessageCreatorDelegate messageCreatorDelegate; + private MessageCreatorDelegate messageCreatorDelegate; public SendDestinationCallback(NmsTemplate jmsTemplate, string destinationName, IMessageCreator messageCreator) { @@ -1352,14 +1453,14 @@ namespace Spring.Messaging.Nms.Core this.messageCreator = messageCreator; } - public SendDestinationCallback(NmsTemplate jmsTemplate, string destinationName, IMessageCreatorDelegate messageCreatorDelegate) + public SendDestinationCallback(NmsTemplate jmsTemplate, string destinationName, MessageCreatorDelegate messageCreatorDelegate) { this.jmsTemplate = jmsTemplate; this.destinationName = destinationName; this.messageCreatorDelegate = messageCreatorDelegate; } - public SendDestinationCallback(NmsTemplate jmsTemplate, IDestination destination, IMessageCreatorDelegate messageCreatorDelegate) + public SendDestinationCallback(NmsTemplate jmsTemplate, IDestination destination, MessageCreatorDelegate messageCreatorDelegate) { this.jmsTemplate = jmsTemplate; this.destination = destination; diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/ProducerDelegate.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/ProducerDelegate.cs new file mode 100644 index 00000000..b71bbe35 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Core/ProducerDelegate.cs @@ -0,0 +1,38 @@ + + +#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.Core +{ + /// Perform operations on the given Session and MessageProducer. + /// The message producer is not associated with any destination. + /// + /// the NMS Session object to use + /// + /// the NMS MessageProducer object to use + /// + /// a result object from working with the Session, if any (can be null) + /// + public delegate object ProducerDelegate(ISession session, IMessageProducer producer); + +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs index 2233deab..065d433c 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs @@ -218,7 +218,7 @@ namespace Spring.Messaging.Nms.Listener /// on some messaging providers. /// Note that Sessions managed by an external transaction manager will /// always get exposed to - /// calls. So in terms of MessageTemplate exposure, this setting only affects + /// calls. So in terms of NmsTemplate exposure, this setting only affects /// locally transacted Sessions. /// /// diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/SimpleMessageListenerContainer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/SimpleMessageListenerContainer.cs index 890297d8..23629da0 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/SimpleMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/SimpleMessageListenerContainer.cs @@ -190,7 +190,6 @@ namespace Spring.Messaging.Nms.Listener { if (this.consumers == null) { - logger.Debug("InitializingConsumers **********"); this.sessions = new HashedSet(); this.consumers = new HashedSet(); IConnection con = SharedConnection; diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/SimpleMessageConverter.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/SimpleMessageConverter.cs index 658b5923..c2bfa10a 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/SimpleMessageConverter.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/SimpleMessageConverter.cs @@ -26,7 +26,7 @@ using Apache.NMS; namespace Spring.Messaging.Nms.Support.Converter { /// A simple message converter that can handle ITextMessages, IBytesMessages, - /// IMapMessages, and IObjectMessages. Used as default by MessageTemplate, for + /// IMapMessages, and IObjectMessages. Used as default by NmsTemplate, for /// ConvertAndSend and ReceiveAndConvert operations. /// ///

Converts a String to a NMS ITextMessage, a byte array to a NMS IBytesMessage, diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/NmsDestinationAccessor.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/NmsDestinationAccessor.cs index ff627255..95f284fc 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/NmsDestinationAccessor.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/NmsDestinationAccessor.cs @@ -23,12 +23,12 @@ using Apache.NMS; namespace Spring.Messaging.Nms.Support.Destinations { - ///

Base class for MessageTemplate} and other + /// Base class for NmsTemplate} and other /// NMS-accessing gateway helpers, adding destination-related properties to /// MessagingAccessor's common properties. /// /// - ///

Not intended to be used directly. See MessageTemplate.

+ ///

Not intended to be used directly. See NmsTemplate.

/// ///
/// Juergen Hoeller diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs index 7e5c4b4e..4d088d95 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs @@ -25,11 +25,11 @@ using Apache.NMS; namespace Spring.Messaging.Nms.Support { - /// Base class for MessageTemplate and other NMS-accessing gateway helpers + /// Base class for NmsTemplate and other NMS-accessing gateway helpers /// It defines common properties like the ConnectionFactory}. The subclass /// NmsIDestinationAccessor adds further, destination-related properties. /// - /// Not intended to be used directly. See MessageTemplate. + /// Not intended to be used directly. See NmsTemplate. /// /// /// Juergen Hoeller diff --git a/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj index 20e4ae35..6898cd1e 100644 --- a/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj +++ b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj @@ -55,8 +55,10 @@ + + diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Core/MessageTemplateTests.cs b/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Core/MessageTemplateTests.cs index cffd2a90..f453a4f0 100644 --- a/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Core/MessageTemplateTests.cs +++ b/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Core/MessageTemplateTests.cs @@ -20,6 +20,7 @@ #region Imports +using System; using System.Collections; using Apache.NMS; using NUnit.Framework; @@ -62,6 +63,7 @@ namespace Spring.Messaging.Nms.Core return template; } + protected virtual bool UseTransactedSession { get { return false; } @@ -163,25 +165,25 @@ namespace Spring.Messaging.Nms.Core }); Assert.AreSame(mockSession, ConnectionFactoryUtils.GetTransactionalSession(scf, null, false)); - Assert.AreSame(mockSession, ConnectionFactoryUtils.GetTransactionalSession(scf, scf.CreateConnection(), 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; - }); + { + bool b = session.Transacted; + return null; + }); IList synchs = TransactionSynchronizationManager.Synchronizations; Assert.AreEqual(1, synchs.Count); - ITransactionSynchronization synch = (ITransactionSynchronization)synchs[0]; + ITransactionSynchronization synch = (ITransactionSynchronization) synchs[0]; synch.BeforeCommit(false); synch.BeforeCompletion(); synch.AfterCommit(); synch.AfterCompletion(TransactionSynchronizationStatus.Unknown); - } finally { diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Integration/SimpleMessageListener.cs b/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Integration/SimpleMessageListener.cs index f0b4cbc0..3e17f923 100644 --- a/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Integration/SimpleMessageListener.cs +++ b/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Integration/SimpleMessageListener.cs @@ -34,6 +34,14 @@ namespace Spring.Messaging.Nms.Integration lastReceivedMessage = message; messageCount++; LOG.Debug("Message listener count = " + messageCount); + ITextMessage textMessage = message as ITextMessage; + if (textMessage != null) + { + LOG.Info("Message Text = " + textMessage.Text); + } else + { + LOG.Warn("Can not process message of type " message.GetType()); + } } #endregion