From b6f610230fe166d4cfe34f3dbb727ed4653c4915 Mon Sep 17 00:00:00 2001 From: markpollack Date: Fri, 25 Jul 2008 04:45:15 +0000 Subject: [PATCH] NMS development --- .../Nms/Connections/CachedMessageProducer.cs | 92 ++++- .../Nms/Connections/CachedSession.cs | 97 +++++- .../Connections/CachingConnectionFactory.cs | 12 +- .../Connections/ChainedExceptionListener.cs | 2 +- .../Nms/Connections/ConnectionFactoryUtils.cs | 184 +++++----- .../Nms/Connections/IDecoratorSession.cs | 6 +- .../Connections/ISmartConnectionFactory.cs | 4 +- .../Nms/Connections/NmsResourceHolder.cs | 98 ++++-- .../Nms/Connections/NmsTransactionManager.cs | 127 ++++++- .../Connections/SingleConnectionFactory.cs | 134 ++++++-- .../SynchedLocalTransactionFailedException.cs | 2 +- .../Messaging/Nms/IExceptionListener.cs | 4 + .../Messaging/Nms/IMessageCreator.cs | 10 +- .../Messaging/Nms/IMessageListener.cs | 7 + .../Messaging/Nms/IMessagePostProcessor.cs | 2 +- .../Messaging/Nms/INmsOperations.cs | 20 +- .../Messaging/Nms/IProducerCallback.cs | 10 +- .../Messaging/Nms/ISessionCallback.cs | 8 +- .../AbstractMessageListenerContainer.cs | 124 ++++++- .../Listener/AbstractNmsListeningContainer.cs | 69 +++- .../Adapter/MessageListenerAdapter.cs | 319 +++++++++++++++--- .../DefaultMessageListenerContainer.cs | 6 - .../Listener/ISessionAwareMessageListener.cs | 40 ++- .../LocallyExposedNmsResourceHolder.cs | 2 +- .../SimpleMessageListenerContainer.cs | 44 ++- .../Messaging/Nms/MessageCreatorDelegate.cs | 6 +- .../Messaging/Nms/NmsGatewaySupport.cs | 17 +- .../Messaging/Nms/NmsTemplate.cs | 182 ++++++---- .../Support/Converter/IMessageConverter.cs | 12 +- .../Converter/MessageConversionException.cs | 34 +- .../Converter/SimpleMessageConverter.cs | 23 +- .../DynamicDestinationResolver.cs | 12 +- .../Destinations/IDestinationResolver.cs | 2 +- .../Messaging/Nms/Support/NmsAccessor.cs | 38 ++- .../Messaging/Nms/Support/NmsUtils.cs | 48 +-- .../Spring.Messaging.Nms.2005.csproj | 2 +- 36 files changed, 1373 insertions(+), 426 deletions(-) delete mode 100644 src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/DefaultMessageListenerContainer.cs diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachedMessageProducer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachedMessageProducer.cs index b9238b47..91b7e078 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachedMessageProducer.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachedMessageProducer.cs @@ -21,7 +21,7 @@ using System; using Apache.NMS; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { /// /// MessageProducer decorator that adapts specific settings @@ -31,7 +31,7 @@ namespace Spring.Messaging.Nms.Connection /// Mark Pollack (.NET) public class CachedMessageProducer : IMessageProducer { - private IMessageProducer target; + private readonly IMessageProducer target; private bool disableMessageID; @@ -51,92 +51,171 @@ namespace Spring.Messaging.Nms.Connection private TimeSpan timeToLive; + /// + /// Initializes a new instance of the class. + /// + /// The target. public CachedMessageProducer(IMessageProducer target) { this.target = target; } + /// + /// Gets the target MessageProducer, the procder we are 'wrapping' + /// + /// The target MessageProducer. public IMessageProducer Target { get { return target; } } + /// + /// Sends the specified message. + /// + /// The message. public void Send(IMessage message) { target.Send(message); } + /// + /// Sends a message to the specified message. + /// + /// The message to send. + /// if set to true use persistent QOS. + /// The message priority. + /// The time to live. public void Send(IMessage message, bool persistent, byte priority, TimeSpan timeToLive) { target.Send(message, persistent, priority, timeToLive); } + /// + /// Sends a message to the specified destination. + /// + /// The destination. + /// The message. public void Send(IDestination destination, IMessage message) { target.Send(destination, message); } + /// + /// Sends a message the specified destination. + /// + /// The destination. + /// The message to send. + /// if set to true use persistent QOS. + /// The priority. + /// The time to live. public void Send(IDestination destination, IMessage message, bool persistent, byte priority, TimeSpan timeToLive) { target.Send(destination, message, persistent, priority, timeToLive); } #region Odd Message Creationg Methods on IMessageProducer - not in-line with JMS APIs. + /// + /// Creates the message. + /// + /// A new message public IMessage CreateMessage() { return target.CreateMessage(); } + /// + /// Creates the text message. + /// + /// A new text message. public ITextMessage CreateTextMessage() { return target.CreateTextMessage(); } + /// + /// Creates the text message. + /// + /// The text. + /// A texst message with the given text. public ITextMessage CreateTextMessage(string text) { return target.CreateTextMessage(text); } + /// + /// Creates the map message. + /// + /// a new map message. public IMapMessage CreateMapMessage() { return target.CreateMapMessage(); } + /// + /// Creates the object message. + /// + /// The body. + /// A new object message with the given body. public IObjectMessage CreateObjectMessage(object body) { return target.CreateObjectMessage(body); } + /// + /// Creates the bytes message. + /// + /// A new bytes message. public IBytesMessage CreateBytesMessage() { return target.CreateBytesMessage(); } + /// + /// Creates the bytes message. + /// + /// The body. + /// A new bytes message with the given body. public IBytesMessage CreateBytesMessage(byte[] body) { return target.CreateBytesMessage(body); } #endregion + /// + /// Gets or sets a value indicating whether this uses a persistent QOS + /// + /// true if persistent; otherwise, false. public bool Persistent { get { return persistent; } set { persistent = value; } } + /// + /// Gets or sets the time to live value for messages sent with this producer. + /// + /// The time to live. public TimeSpan TimeToLive { get { return timeToLive; } set { timeToLive = value; } } + /// + /// Gets or sets the priority of messages sent with this producer. + /// + /// The priority. public byte Priority { get { return priority; } set { priority = value;} } + /// + /// Gets or sets a value indicating whether disable setting of the message ID property. + /// + /// true if disable message ID setting; otherwise, false. public bool DisableMessageID { get @@ -153,6 +232,12 @@ namespace Spring.Messaging.Nms.Connection } } + /// + /// Gets or sets a value indicating whether disable setting the message timestamp property. + /// + /// + /// true if disable message timestamp; otherwise, false. + /// public bool DisableMessageTimestamp { get @@ -169,6 +254,9 @@ namespace Spring.Messaging.Nms.Connection } } + /// + /// Reset properties. + /// public void Dispose() { // It's a cached MessageProducer... reset properties only. diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachedSession.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachedSession.cs index 6c67e40f..8d57fb65 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachedSession.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachedSession.cs @@ -24,10 +24,10 @@ using Common.Logging; using Spring.Collections; using IQueue=Apache.NMS.IQueue; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { /// - /// Wrapper for ISession that caches producers and registers itself as available + /// Wrapper for Session that caches producers and registers itself as available /// to the session cache when being closed. Generally used for testing purposes or /// if need to get at the wrapped Session object via the TargetSession property (for /// vendor specific methods). @@ -192,101 +192,194 @@ namespace Spring.Messaging.Nms.Connection } #region Pass through implementations + /// + /// Creates the consumer. + /// + /// The destination. + /// public IMessageConsumer CreateConsumer(IDestination destination) { return target.CreateConsumer(destination); } + /// + /// Creates the consumer. + /// + /// The destination. + /// The selector. + /// public IMessageConsumer CreateConsumer(IDestination destination, string selector) { return target.CreateConsumer(destination, selector); } + /// + /// Creates the consumer. + /// + /// The destination. + /// The selector. + /// if set to true [no local]. + /// public IMessageConsumer CreateConsumer(IDestination destination, string selector, bool noLocal) { return target.CreateConsumer(destination, selector, noLocal); } + + /// + /// Creates the durable consumer. + /// + /// The destination. + /// The name. + /// The selector. + /// if set to true [no local]. + /// public IMessageConsumer CreateDurableConsumer(ITopic destination, string name, string selector, bool noLocal) { return target.CreateDurableConsumer(destination, name, selector, noLocal); } + /// + /// Gets the queue. + /// + /// The name. + /// public IQueue GetQueue(string name) { return target.GetQueue(name); } + /// + /// Gets the topic. + /// + /// The name. + /// public ITopic GetTopic(string name) { return target.GetTopic(name); } + /// + /// Creates the temporary queue. + /// + /// public ITemporaryQueue CreateTemporaryQueue() { return target.CreateTemporaryQueue(); } + /// + /// Creates the temporary topic. + /// + /// public ITemporaryTopic CreateTemporaryTopic() { return target.CreateTemporaryTopic(); } + /// + /// Creates the message. + /// + /// public IMessage CreateMessage() { return target.CreateMessage(); } + /// + /// Creates the text message. + /// + /// public ITextMessage CreateTextMessage() { return target.CreateTextMessage(); } + /// + /// Creates the text message. + /// + /// The text. + /// public ITextMessage CreateTextMessage(string text) { return target.CreateTextMessage(text); } + /// + /// Creates the map message. + /// + /// public IMapMessage CreateMapMessage() { return target.CreateMapMessage(); } + /// + /// Creates the object message. + /// + /// The body. + /// public IObjectMessage CreateObjectMessage(object body) { return target.CreateObjectMessage(body); } + /// + /// Creates the bytes message. + /// + /// public IBytesMessage CreateBytesMessage() { return target.CreateBytesMessage(); } + /// + /// Creates the bytes message. + /// + /// The body. + /// public IBytesMessage CreateBytesMessage(byte[] body) { return target.CreateBytesMessage(body); } + /// + /// Commits this instance. + /// public void Commit() { target.Commit(); } + /// + /// Rollbacks this instance. + /// public void Rollback() { target.Rollback(); } + /// + /// Gets a value indicating whether this is transacted. + /// + /// true if transacted; otherwise, false. public bool Transacted { get { return target.Transacted; } } + /// + /// Gets the acknowledgement mode. + /// + /// The acknowledgement mode. public AcknowledgementMode AcknowledgementMode { get { return target.AcknowledgementMode; } } + /// + /// Call dispose on the target. + /// public void Dispose() { target.Dispose(); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachingConnectionFactory.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachingConnectionFactory.cs index b27ec9d4..3c42cfe6 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachingConnectionFactory.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/CachingConnectionFactory.cs @@ -25,11 +25,11 @@ using Spring.Collections; using Spring.Util; using IQueue=Apache.NMS.IQueue; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { /// /// subclass that adds - /// ISession and IMessageProducer caching. This ConnectionFactory + /// Session and MessageProducer caching. This ConnectionFactory /// also switches the ReconnectOnException property to true /// by default, allowing for automatic recovery of the underlying /// Connection. @@ -169,6 +169,14 @@ namespace Spring.Messaging.Nms.Connection return session; } + /// + /// Wraps the given Session so that it delegates every method call to the target session but + /// adapts close calls. This is useful for allowing application code to + /// handle a special framework Session just like an ordinary Session. + /// + /// The original Session to wrap. + /// The List of cached Sessions that the given Session belongs to. + /// The wrapped Session protected virtual ISession GetCachedSessionWrapper(ISession targetSession, LinkedList sessionList) { return new CachedSession(targetSession, sessionList, SessionCacheSize, CacheProducers); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ChainedExceptionListener.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ChainedExceptionListener.cs index 30889c66..acb0b1f0 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ChainedExceptionListener.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ChainedExceptionListener.cs @@ -23,7 +23,7 @@ using System; using System.Collections; using Spring.Util; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { /// /// Implementation of Spring IExceptionListener interface that supports diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs index b14b04df..7f22bf88 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs @@ -21,14 +21,14 @@ using System; using Apache.NMS; using Common.Logging; +using Spring.Messaging.Nms.Support; using Spring.Transaction.Support; using Spring.Util; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { /// Helper class for obtaining transactional NMS resources - /// for a given IConnectionFactory. - /// + /// for a given ConnectionFactory. /// /// Juergen Hoeller /// Mark Pollack (.NET) @@ -40,19 +40,76 @@ namespace Spring.Messaging.Nms.Connection #endregion - /// Obtain a NMS ISession that is synchronized with the current transaction, if any. - /// the IConnectionFactory to obtain a ISession for + /// + /// Releases the given connection, stopping it (if necessary) and eventually closing it. + /// + /// Checks , if available. + /// This is essentially a more sophisticated version of + /// + /// + /// The connection to release. (if this is null, the call will be ignored) + /// The ConnectionFactory that the Connection was obtained from. (may be null) + /// whether the Connection might have been started by the application. + public static void ReleaseConnection(IConnection connection, IConnectionFactory cf, bool started) + { + if (connection == null) + { + return; + } + + if (started && cf is ISmartConnectionFactory && ((ISmartConnectionFactory)cf).ShouldStop(connection)) + { + try + { + connection.Stop(); + } + catch (Exception ex) + { + LOG.Debug("Could not stop NMS Connection before closing it", ex); + + } + } + try + { + connection.Close(); + } catch (Exception ex) + { + LOG.Debug("Could not close NMS Connection", ex); + } + } + + /// + /// Determines whether the given JMS Session is transactional, that is, + /// bound to the current thread by Spring's transaction facilities. + /// + /// The session to check. + /// The ConnectionFactory that the Session originated from + /// + /// true if is session transactional, bound to current thread; otherwise, false. + /// + public static bool IsSessionTransactional(ISession session, IConnectionFactory cf) + { + if (session == null || cf == null) + { + return false; + } + NmsResourceHolder resourceHolder = (NmsResourceHolder) TransactionSynchronizationManager.GetResource(cf); + return (resourceHolder != null && resourceHolder.ContainsSession(session)); + } + + /// Obtain a NMS Session that is synchronized with the current transaction, if any. + /// the ConnectionFactory to obtain a Session for /// - /// the existing NMS IConnection to obtain a ISession for + /// the existing NMS Connection to obtain a Session for /// (may be null) /// /// whether to allow for a local NMS transaction /// that is synchronized with a Spring-managed transaction (where the main transaction /// might be a ADO.NET-based one for a specific DataSource, for example), with the NMS /// transaction committing right after the main transaction. If not allowed, the given - /// IConnectionFactory needs to handle transaction enlistment underneath the covers. + /// ConnectionFactory needs to handle transaction enlistment underneath the covers. /// - /// the transactional ISession, or null if none found + /// the transactional Session, or null if none found /// /// NMSException in case of NMS failure public static ISession GetTransactionalSession(IConnectionFactory cf, IConnection existingCon, @@ -61,20 +118,24 @@ namespace Spring.Messaging.Nms.Connection return DoGetTransactionalSession(cf, new AnonymousClassResourceFactory(existingCon, cf, - synchedLocalTransactionAllowed)); + synchedLocalTransactionAllowed), true); } - /// Obtain a NMS ISession that is synchronized with the current transaction, if any. + /// + /// Obtain a NMS Session that is synchronized with the current transaction, if any. + /// /// the TransactionSynchronizationManager key to bind to - /// (usually the IConnectionFactory) - /// + /// (usually the ConnectionFactory) /// the ResourceFactory to use for extracting or creating - /// NMS resources - /// - /// the transactional ISession, or null if none found + /// NMS resources + /// whether the underlying Connection approach should be + /// started in order to allow for receiving messages. Note that a reused Connection + /// may already have been started before, even if this flag is false. + /// + /// the transactional Session, or null if none found /// /// NMSException in case of NMS failure - public static ISession DoGetTransactionalSession(Object resourceKey, ResourceFactory resourceFactory) + public static ISession DoGetTransactionalSession(Object resourceKey, ResourceFactory resourceFactory, bool startConnection) { AssertUtils.ArgumentNotNull(resourceKey, "Resource key must not be null"); AssertUtils.ArgumentNotNull(resourceKey, "ResourceFactory must not be null"); @@ -83,22 +144,31 @@ namespace Spring.Messaging.Nms.Connection (NmsResourceHolder)TransactionSynchronizationManager.GetResource(resourceKey); if (resourceHolder != null) { - ISession rssession = resourceFactory.GetSession(resourceHolder); - if (rssession != null || resourceHolder.Frozen) + ISession rhSession = resourceFactory.GetSession(resourceHolder); + if (rhSession != null) { - return rssession; + if (startConnection) + { + IConnection conn = resourceFactory.GetConnection(resourceHolder); + if (conn != null) + { + conn.Start(); + } + } + return rhSession; } } if (!TransactionSynchronizationManager.SynchronizationActive) { return null; } - NmsResourceHolder conHolderToUse = resourceHolder; - if (conHolderToUse == null) + NmsResourceHolder resourceHolderToUse = resourceHolder; + if (resourceHolderToUse == null) { - conHolderToUse = new NmsResourceHolder(); + resourceHolderToUse = new NmsResourceHolder(); } - IConnection con = resourceFactory.GetConnection(conHolderToUse); + + IConnection con = resourceFactory.GetConnection(resourceHolderToUse); ISession session = null; try { @@ -106,11 +176,11 @@ namespace Spring.Messaging.Nms.Connection if (!isExistingCon) { con = resourceFactory.CreateConnection(); - conHolderToUse.AddConnection(con); + resourceHolderToUse.AddConnection(con); } session = resourceFactory.CreateSession(con); - conHolderToUse.AddSession(session, con); - if (!isExistingCon) + resourceHolderToUse.AddSession(session, con); + if (startConnection) { con.Start(); } @@ -141,55 +211,17 @@ namespace Spring.Messaging.Nms.Connection } throw; } - if (conHolderToUse != resourceHolder) + if (resourceHolderToUse != resourceHolder) { TransactionSynchronizationManager.RegisterSynchronization( - new NmsResourceSynchronization(resourceKey, conHolderToUse, + new NmsResourceSynchronization(resourceKey, resourceHolderToUse, resourceFactory.SynchedLocalTransactionAllowed)); - conHolderToUse.SynchronizedWithTransaction = true; - TransactionSynchronizationManager.BindResource(resourceKey, conHolderToUse); + resourceHolderToUse.SynchronizedWithTransaction = true; + TransactionSynchronizationManager.BindResource(resourceKey, resourceHolderToUse); } return session; } - public static void ReleaseConnection(IConnection connection, IConnectionFactory cf, bool started) - { - if (connection == null) - { - return; - } - - if (started && cf is ISmartConnectionFactory && ((ISmartConnectionFactory)cf).ShouldStop(connection)) - { - try - { - connection.Stop(); - } - catch (Exception ex) - { - LOG.Debug("Could not stop NMS IConnection before closing it", ex); - - } - } - try - { - connection.Close(); - } catch (Exception ex) - { - LOG.Debug("Could not close NMS Connection", ex); - } - } - - public static bool IsSessionTransactional(ISession session, IConnectionFactory cf) - { - if (session == null || cf == null) - { - return false; - } - NmsResourceHolder resourceHolder = (NmsResourceHolder) TransactionSynchronizationManager.GetResource(cf); - return (resourceHolder != null && resourceHolder.ContainsSession(session)); - } - #region ResourceFactory helper classes private class AnonymousClassResourceFactory : ResourceFactory @@ -253,32 +285,32 @@ namespace Spring.Messaging.Nms.Connection /// public interface ResourceFactory { - /// Fetch an appropriate ISession from the given NmsResourceHolder. + /// Fetch an appropriate Session from the given NmsResourceHolder. /// the NmsResourceHolder /// - /// an appropriate ISession fetched from the holder, + /// an appropriate Session fetched from the holder, /// or null if none found /// ISession GetSession(NmsResourceHolder holder); - /// Fetch an appropriate IConnection from the given NmsResourceHolder. + /// Fetch an appropriate Connection from the given NmsResourceHolder. /// the NmsResourceHolder /// - /// an appropriate IConnection fetched from the holder, + /// an appropriate Connection fetched from the holder, /// or null if none found /// IConnection GetConnection(NmsResourceHolder holder); - /// Create a new NMS IConnection for registration with a NmsResourceHolder. - /// the new NMS IConnection + /// Create a new NMS Connection for registration with a NmsResourceHolder. + /// the new NMS Connection /// /// NMSException if thrown by NMS API methods IConnection CreateConnection(); /// Create a new NMS ISession for registration with a NmsResourceHolder. - /// the NMS IConnection to create a ISession for + /// the NMS Connection to create a ISession for /// - /// the new NMS ISession + /// the new NMS Session /// /// NMSException if thrown by NMS API methods ISession CreateSession(IConnection con); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/IDecoratorSession.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/IDecoratorSession.cs index 8e705824..87f3738e 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/IDecoratorSession.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/IDecoratorSession.cs @@ -20,11 +20,11 @@ using Apache.NMS; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { /// - /// Subinterface of ISession to be implemented by - /// implementations that wrap an ISession to provide added + /// Subinterface of Session to be implemented by + /// implementations that wrap an Session to provide added /// functionality. Allows access to the the underlying target Session. /// /// Mark Pollack diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ISmartConnectionFactory.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ISmartConnectionFactory.cs index 2911c003..822db229 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ISmartConnectionFactory.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ISmartConnectionFactory.cs @@ -22,10 +22,10 @@ using Apache.NMS; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { /// - /// Extension of the IConnectionFactory interface, + /// Extension of the ConnectionFactory interface, /// indicating how to release Connections obtained from it. /// /// Juergen Hoeller diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs index 16ba704f..9964efc7 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs @@ -26,11 +26,11 @@ using Spring.Transaction.Support; using Spring.Util; using Apache.NMS; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { - /// IConnection holder, wrapping a NMS IConnection and a NMS ISession. + /// Connection holder, wrapping a NMS Connection and a NMS Session. /// NmsTransactionManager binds instances of this class to the thread, - /// for a given NMS IConnectionFactory. + /// for a given NMS ConnectionFactory. /// ///

Note: This is an SPI class, not intended to be used by applications.

/// @@ -92,17 +92,23 @@ namespace Spring.Messaging.Nms.Connection } /// Create a new NmsResourceHolder for the given NMS resources. - /// the NMS IConnection + /// the NMS Connection /// - /// the NMS ISession + /// the NMS Session /// - public NmsResourceHolder(Apache.NMS.IConnection connection, ISession session) + public NmsResourceHolder(IConnection connection, ISession session) { AddConnection(connection); AddSession(session, connection); this.frozen = true; } + /// + /// Initializes a new instance of the class. + /// + /// The connection factory. + /// The connection. + /// The session. public NmsResourceHolder(IConnectionFactory connectionFactory, IConnection connection, ISession session) { this.connectionFactory = connectionFactory; @@ -114,6 +120,12 @@ namespace Spring.Messaging.Nms.Connection #region Properties + /// + /// Gets a value indicating whether this is frozen, namely that + /// additional resources can be registered with the holder. If using any of the constructors with + /// a Session, the holder will be set to the frozen state. + /// + /// true if frozen; otherwise, false. virtual public bool Frozen { get @@ -125,11 +137,14 @@ namespace Spring.Messaging.Nms.Connection #endregion #region Methods - - public void AddConnection(Apache.NMS.IConnection connection) + + /// + /// Adds the connection to the list of resources managed by this holder. + /// + /// The connection. + public void AddConnection(IConnection connection) { - //TODO - update Assert utility class... - //Assert.isTrue(!this.frozen, "Cannot add IConnection because NmsResourceHolder is frozen"); + AssertUtils.IsTrue(!frozen, "Cannot add IConnection because NmsResourceHolder is frozen"); AssertUtils.ArgumentNotNull(connection, "IConnection must not be null"); if (!connections.Contains(connection)) { @@ -137,15 +152,23 @@ namespace Spring.Messaging.Nms.Connection } } + /// + /// Adds the session to the list of resources managed by this holder. + /// + /// The session. public void AddSession(ISession session) { AddSession(session, null); } - public void AddSession(ISession session, Apache.NMS.IConnection connection) + /// + /// Adds the session and connection to the list of resources managed by this holder. + /// + /// The session. + /// The connection. + public void AddSession(ISession session, IConnection connection) { - //TOOD update AssertUtils class - //Assert.isTrue(!this.frozen, "Cannot add ISession because NmsResourceHolder is frozen"); + AssertUtils.IsTrue(!frozen, "Cannot add ISession because NmsResourceHolder is frozen"); AssertUtils.ArgumentNotNull(session, "ISession must not be null"); if (!sessions.Contains(session)) { @@ -163,38 +186,60 @@ namespace Spring.Messaging.Nms.Connection } } - public virtual Apache.NMS.IConnection GetConnection() + /// + /// Gets the connection managed by this resource holder + /// + /// A Connection, or null if no managed connection. + public virtual IConnection GetConnection() { - return (!(this.connections.Count == 0) ? (Apache.NMS.IConnection)this.connections[0] : null); + return (!(this.connections.Count == 0) ? (IConnection)this.connections[0] : null); } - public virtual Apache.NMS.IConnection GetConnection(System.Type connectionType) + /// + /// Gets the connection of a given type managed by this resource holder. This is used + /// when storing Queue or Topic Connections (from the older 1.0.2 API) as compared to the + /// 'unified domain' API , just Connection, in the newer 1.2 API. + /// + /// Type of the connection. + /// The connection, or null if not found. + public virtual IConnection GetConnection(Type connectionType) { - throw new NotImplementedException(); - //TODO Updae CollectionUtils... - //return (NMS.IConnection)CollectionUtils.FindValueOfType(this.connections, connectionType); + return (IConnection)CollectionUtils.FindValueOfType(this.connections, connectionType); } + /// + /// Gets the first session manged by this holder or null if not available. + /// + /// The session or null if not available. public virtual ISession GetSession() { return (!(this.sessions.Count == 0) ? (ISession)this.sessions[0] : null); } + /// + /// Gets the session managed by this holder by type. + /// + /// Type of the session. + /// The session or null if not available. public virtual ISession GetSession(Type sessionType) { return GetSession(sessionType, null); } - public virtual ISession GetSession(System.Type sessionType, Apache.NMS.IConnection connection) + /// + /// Gets the session of a given type associated with the given connection + /// + /// Type of the session. + /// The connection. + /// The sessin or null if not available. + public virtual ISession GetSession(Type sessionType, IConnection connection) { IList sessions = this.sessions; if (connection != null) { sessions = (IList)sessionsPerIConnection[connection]; } - throw new NotImplementedException(); - //TODO update collection utils - //return (ISession)CollectionUtils.FindValueOfType(sessions, sessionType); + return (ISession)CollectionUtils.FindValueOfType(sessions, sessionType); } /// @@ -230,6 +275,13 @@ namespace Spring.Messaging.Nms.Connection } } + /// + /// Determines whether the holder contains the specified session. + /// + /// The session. + /// + /// true if the holder contains the specified session; otherwise, false. + /// public bool ContainsSession(ISession session) { return this.sessions.Contains(session); 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 b018e9e2..e3a6b294 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsTransactionManager.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsTransactionManager.cs @@ -1,3 +1,22 @@ +#region License + +/* + * Copyright 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion using System; @@ -8,11 +27,11 @@ using Spring.Objects.Factory; using Spring.Transaction; using Spring.Transaction.Support; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { /// /// A implementation - /// for a single NMS Apache.NMS.IConnectionFactory. Binds a + /// for a single NMS ConnectionFactory. Binds a /// Connection/Session pair from the specified ConnecctionFactory to the thread, /// potentially allowing for one thread-bound Session per ConnectionFactory. /// @@ -91,7 +110,6 @@ namespace Spring.Messaging.Nms.Connection get { return connectionFactory; } set { - //TODO if create TransactionAwareConnectionFactoryProxy need to check for it here. connectionFactory = value; } } @@ -126,6 +144,10 @@ namespace Spring.Messaging.Nms.Connection #endregion + /// + /// Get the NmsTransactionObject. + /// + /// he NmsTransactionObject. protected override object DoGetTransaction() { NmsTransactionObject txObject = new NmsTransactionObject(); @@ -135,6 +157,21 @@ namespace Spring.Messaging.Nms.Connection return txObject; } + + /// + /// Begin a new transaction with the given transaction definition. + /// + /// Transaction object returned by + /// . + /// instance, describing + /// propagation behavior, isolation level, timeout etc. + /// + /// Does not have to care about applying the propagation behavior, + /// as this has already been handled by this abstract manager. + /// + /// + /// In the case of creation or system errors. + /// protected override void DoBegin(object transaction, ITransactionDefinition definition) { //This is the default value defined in DefaultTransactionDefinition @@ -186,6 +223,21 @@ namespace Spring.Messaging.Nms.Connection } + /// + /// Suspend the resources of the current transaction. + /// + /// Transaction object returned by + /// . + /// + /// An object that holds suspended resources (will be kept unexamined for passing it into + /// .) + /// + /// + /// Transaction synchronization will already have been suspended. + /// + /// + /// in case of system errors. + /// protected override object DoSuspend(object transaction) { NmsTransactionObject txObject = (NmsTransactionObject) transaction; @@ -193,12 +245,31 @@ namespace Spring.Messaging.Nms.Connection return TransactionSynchronizationManager.UnbindResource(ConnectionFactory); } + /// + /// Resume the resources of the current transaction. + /// + /// Transaction object returned by + /// . + /// The object that holds suspended resources as returned by + /// . + /// Transaction synchronization will be resumed afterwards. + /// + /// + /// In the case of system errors. + /// protected override void DoResume(object transaction, object suspendedResources) { NmsResourceHolder conHolder = (NmsResourceHolder) suspendedResources; TransactionSynchronizationManager.BindResource(ConnectionFactory, conHolder); } + /// + /// Perform an actual commit on the given transaction. + /// + /// The status representation of the transaction. + /// + /// In the case of system errors. + /// protected override void DoCommit(DefaultTransactionStatus status) { NmsTransactionObject txObject = (NmsTransactionObject)status.Transaction; @@ -210,19 +281,22 @@ namespace Spring.Messaging.Nms.Connection LOG.Debug("Committing NMS transaction on Session [" + session + "]"); } session.Commit(); - /** https://issues.apache.org/activemq/browse/AMQNET-93 - TODO - need to mirror JMS exception classes in NMS API. - } catch (TransactionRolledBackException ex) - { - */ - + //Note that NMS does not have, TransactionRolledBackException + //See https://issues.apache.org/activemq/browse/AMQNET-93 } catch (NMSException ex) - { + { throw new TransactionSystemException("Could not commit NMS transaction.", ex); } } + /// + /// Perform an actual rollback on the given transaction. + /// + /// The status representation of the transaction. + /// + /// In the case of system errors. + /// protected override void DoRollback(DefaultTransactionStatus status) { NmsTransactionObject txObject = (NmsTransactionObject)status.Transaction; @@ -242,12 +316,33 @@ namespace Spring.Messaging.Nms.Connection } + /// + /// Set the given transaction rollback-only. Only called on rollback + /// if the current transaction takes part in an existing one. + /// + /// The status representation of the transaction. + /// + /// In the case of system errors. + /// protected override void DoSetRollbackOnly(DefaultTransactionStatus status) { NmsTransactionObject txObject = (NmsTransactionObject)status.Transaction; txObject.ResourceHolder.RollbackOnly = true; } + /// + /// Cleanup resources after transaction completion. + /// + /// Transaction object returned by + /// . + /// + /// + /// Called after + /// and + /// + /// execution on any outcome. + /// + /// protected override void DoCleanupAfterCompletion(object transaction) { NmsTransactionObject txObject = (NmsTransactionObject)transaction; @@ -256,6 +351,18 @@ namespace Spring.Messaging.Nms.Connection txObject.ResourceHolder.Clear(); } + /// + /// Check if the given transaction object indicates an existing transaction + /// (that is, a transaction which has already started). + /// + /// Transaction object returned by + /// . + /// + /// True if there is an existing transaction. + /// + /// + /// In the case of system errors. + /// protected override bool IsExistingTransaction(object transaction) { NmsTransactionObject txObject = transaction as NmsTransactionObject; diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SingleConnectionFactory.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SingleConnectionFactory.cs index 93f6ced9..7d5b1474 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SingleConnectionFactory.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SingleConnectionFactory.cs @@ -21,11 +21,36 @@ using System; using Apache.NMS; using Common.Logging; +using Spring.Messaging.Nms; using Spring.Objects.Factory; using Spring.Util; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { + /// + /// A ConnectionFactory adapter that returns the same Connection + /// from all CreateConnection() calls, and ignores calls to + /// Connection.Close(). According to the JMS Connection + /// model, this is perfectly thread-safe, check your vendor implmenetation for + /// details. + /// + /// + /// You can either pass in a specific Connection directly or let this + /// factory lazily create a Connection via a given target ConnectionFactory. + /// Useful in order to keep using the same Connection for multiple + /// calls, without having a pooling ConnectionFactory + /// underneath. This may span any number of transactions, even concurrently executing transactions. + /// + /// + /// Note that Spring's message listener containers support the use of + /// a shared Connection within each listener container instance. Using + /// SingleConnectionFactory with a MessageListenerContainer only really makes sense for + /// sharing a single Connection across multiple listener containers. + /// + /// + /// Juergen Hoeller + /// Mark Pollack + /// Mark Pollack (.NET) public class SingleConnectionFactory : IConnectionFactory, IExceptionListener, IInitializingObject, IDisposable { #region Logging Definition @@ -80,7 +105,7 @@ namespace Spring.Messaging.Nms.Connection { AssertUtils.ArgumentNotNull(target, "connection", "TargetSession Connection must not be null"); this.target = target; - connection = GetSharedConnection(this, target); + connection = GetSharedConnection(target); } @@ -127,6 +152,11 @@ namespace Spring.Messaging.Nms.Connection } + /// + /// Gets or sets the exception listener implementation that should be registered + /// with with the single Connection created by this factory, if any. + /// + /// The exception listener. public IExceptionListener ExceptionListener { get { return exceptionListener; } @@ -162,6 +192,10 @@ namespace Spring.Messaging.Nms.Connection #region IConnectionFactory Members + /// + /// Creates the connection. + /// + /// A single shared connection public IConnection CreateConnection() { lock (connectionMonitor) @@ -174,6 +208,12 @@ namespace Spring.Messaging.Nms.Connection } } + /// + /// Creates the connection. + /// + /// Name of the user. + /// The password. + /// public IConnection CreateConnection(string userName, string password) { throw new InvalidOperationException("SingleConnectionFactory does not support custom username and password."); @@ -181,6 +221,10 @@ namespace Spring.Messaging.Nms.Connection #endregion + /// + /// Initialize the underlying shared Connection. Closes and reinitializes the Connection if an underlying + /// Connection is present already. + /// public void InitConnection() { if (TargetConnectionFactory == null) @@ -200,7 +244,7 @@ namespace Spring.Messaging.Nms.Connection { LOG.Info("Established shared NMS Connection: " + this.target); } - this.connection = GetSharedConnection(this, target); + this.connection = GetSharedConnection(target); } } @@ -213,6 +257,13 @@ namespace Spring.Messaging.Nms.Connection ResetConnection(); } + /// + /// Prepares the connection before it is exposed. + /// The default implementation applies ExceptionListener and client id. + /// Can be overridden in subclasses. + /// + /// The Connection to prepare. + /// if thrown by any NMS API methods. protected virtual void PrepareConnection(IConnection con) { if (ClientId != null) @@ -249,11 +300,19 @@ namespace Spring.Messaging.Nms.Connection return null; } + /// + /// reate a JMS Connection via this template's ConnectionFactory. + /// + /// protected virtual IConnection DoCreateConnection() { return TargetConnectionFactory.CreateConnection(); } + /// + /// Closes the given connection. + /// + /// The connection. protected virtual void CloseConnection(IConnection con) { try @@ -273,6 +332,9 @@ namespace Spring.Messaging.Nms.Connection #region IInitializingObject Members + /// + /// Ensure that the connection or TargetConnectionFactory are specified. + /// public void AfterPropertiesSet() { if (connection == null && TargetConnectionFactory == null) @@ -283,11 +345,20 @@ namespace Spring.Messaging.Nms.Connection #endregion + + /// + /// Close the underlying shared connection. The provider of this ConnectionFactory needs to care for proper shutdown. + /// As this object implements an application context will automatically + /// invoke this on distruction o + /// public void Dispose() { ResetConnection(); } + /// + /// Resets the underlying shared Connection, to be reinitialized on next access. + /// public virtual void ResetConnection() { lock (connectionMonitor) @@ -301,11 +372,19 @@ namespace Spring.Messaging.Nms.Connection } } - protected virtual IConnection GetSharedConnection(SingleConnectionFactory singleConnectionFactory, IConnection target) + /// + /// Wrap the given Connection with a proxy that delegates every method call to it + /// but suppresses close calls. This is useful for allowing application code to + /// handle a special framework Connection just like an ordinary Connection from a + /// ConnectionFactory. + /// + /// The original connection to wrap. + /// the wrapped connection + protected virtual IConnection GetSharedConnection(IConnection target) { lock (connectionMonitor) { - return new CloseSupressingConnection(singleConnectionFactory, target); + return new CloseSupressingConnection(this, target); } } } @@ -340,6 +419,26 @@ namespace Spring.Messaging.Nms.Connection this.singleConnectionFactory = singleConnectionFactory; } + public string ClientId + { + get { return target.ClientId; } + set + { + string currentClientId = target.ClientId; + if (currentClientId != null && currentClientId.Equals(value)) + { + //ok, the values are consistent. + } + else + { + throw new ArgumentException( + "Setting of 'ClientID' property not supported on wrapper for shared Connection." + + "Set the 'ClientId' property on the SingleConnectionFactory instead."); + } + + } + } + public void Close() { // don't pass the call to the target. @@ -370,7 +469,10 @@ namespace Spring.Messaging.Nms.Connection public event ExceptionListener ExceptionListener { - add { target.ExceptionListener += value; } + add + { + target.ExceptionListener += value; + } remove { target.ExceptionListener -= value; } } @@ -381,26 +483,6 @@ namespace Spring.Messaging.Nms.Connection set { target.AcknowledgementMode = value; } } - public string ClientId - { - get { return target.ClientId; } - set - { - string currentClientId = target.ClientId; - if (currentClientId != null && currentClientId.Equals(value)) - { - //ok - } - else - { - throw new ArgumentException( - "Setting of 'ClientID' property not supported on wrapper for shared Connection." + - "Set the 'ClientId' property on the SingleConnectionFactory instead."); - } - - } - } - public void Dispose() { target.Dispose(); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SynchedLocalTransactionFailedException.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SynchedLocalTransactionFailedException.cs index ce3ebcf4..730910fd 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SynchedLocalTransactionFailedException.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SynchedLocalTransactionFailedException.cs @@ -21,7 +21,7 @@ using System; using Apache.NMS; -namespace Spring.Messaging.Nms.Connection +namespace Spring.Messaging.Nms.Connections { /// Exception thrown when a synchronized local transaction failed to complete /// (after the main transaction has already completed). diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IExceptionListener.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IExceptionListener.cs index 35150487..8953b07a 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IExceptionListener.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IExceptionListener.cs @@ -28,6 +28,10 @@ namespace Spring.Messaging.Nms /// Mark Pollack public interface IExceptionListener { + /// + /// Called when there is an exception in message processing. + /// + /// The exception. void OnException(Exception exception); } } \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageCreator.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageCreator.cs index 4b2937ba..40db1d44 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageCreator.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageCreator.cs @@ -22,19 +22,19 @@ using Apache.NMS; namespace Spring.Messaging.Nms { - /// Creates a NMS message given a ISession + /// Creates a NMS message given a Session /// - ///

The ISession typically is provided by an instance + ///

The Session typically is provided by an instance /// of the NmsTemplate class.

///
/// Mark Pollack public interface IMessageCreator { - /// Create a IMessage to be sent. - /// the NMS ISession to be used to create the + /// Create a Message to be sent. + /// the NMS Session to be used to create the /// IMessage (never null) /// - /// the IMessage to be sent + /// the Message to be sent /// /// NMSException if thrown by NMS API methods IMessage CreateMessage(ISession session); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageListener.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageListener.cs index 9254681d..1728c835 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageListener.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessageListener.cs @@ -22,8 +22,15 @@ using Apache.NMS; namespace Spring.Messaging.Nms { + /// + /// Interfaced based approach to listen to messaging events. + /// public interface IMessageListener { + /// + /// Called when a message is delivered. + /// + /// The message. void OnMessage(IMessage message); } } \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessagePostProcessor.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessagePostProcessor.cs index cf9e4eda..3aadd167 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessagePostProcessor.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IMessagePostProcessor.cs @@ -37,7 +37,7 @@ namespace Spring.Messaging.Nms ///
/// the NMS message from the IMessageConverter /// - /// the modified version of the IMessage + /// the modified version of the Message /// /// NMSException if thrown by NMS API methods IMessage PostProcessMessage(IMessage message); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/INmsOperations.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/INmsOperations.cs index 1d561ebe..5dc54305 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/INmsOperations.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/INmsOperations.cs @@ -39,11 +39,13 @@ namespace Spring.Messaging.Nms public interface INmsOperations { /// Execute the action specified by the given action object within - /// a NMS ISession. - ///

Note: The value of isPubSubDomain affects the behavior of this method. - /// If isPubSubDomain equals true, then a ISession is passed to the callback. - /// If false, then a ISession is passed to the callback.

+ /// 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 @@ -52,7 +54,7 @@ namespace Spring.Messaging.Nms object Execute(ISessionCallback action); /// Send a message to a NMS destination. The callback gives access to - /// the NMS session and IMessageProducer in order to do more complex + /// the NMS session and MessageProducer in order to do more complex /// send operations. /// /// callback object that exposes the session/producer pair @@ -76,7 +78,7 @@ namespace Spring.Messaging.Nms void Send(IMessageCreator messageCreator); /// Send a message to the specified destination. - /// The IMessageCreator callback creates the message given a ISession. + /// The IMessageCreator callback creates the message given a Session. /// /// the destination to send this message to /// @@ -86,7 +88,7 @@ namespace Spring.Messaging.Nms void Send(IDestination destination, IMessageCreator messageCreator); /// Send a message to the specified destination. - /// The IMessageCreator callback creates the message given a ISession. + /// The IMessageCreator callback creates the message given a Session. /// /// the name of the destination to send this message to /// (to be resolved to an actual destination by a DestinationResolver) @@ -109,7 +111,7 @@ namespace Spring.Messaging.Nms void SendWithDelegate(IMessageCreatorDelegate messageCreatorDelegate); /// Send a message to the specified destination. - /// The IMessageCreator callback creates the message given a ISession. + /// The IMessageCreator callback creates the message given a Session. /// /// the destination to send this message to /// @@ -119,7 +121,7 @@ namespace Spring.Messaging.Nms void SendWithDelegate(IDestination destination, IMessageCreatorDelegate messageCreatorDelegate); /// Send a message to the specified destination. - /// The IMessageCreator callback creates the message given a ISession. + /// The IMessageCreator callback creates the message given a Session. /// /// the name of the destination to send this message to /// (to be resolved to an actual destination by a DestinationResolver) diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IProducerCallback.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IProducerCallback.cs index e0b62942..d3d909cc 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IProducerCallback.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/IProducerCallback.cs @@ -28,19 +28,19 @@ namespace Spring.Messaging.Nms /// method, often implemented as an anonymous inner class.

/// ///

The typical implementation will perform multiple operations on the - /// supplied NMS ISession and IMessageProducer.

+ /// supplied NMS Session and MessageProducer.

/// /// Mark Pollack public interface IProducerCallback { - /// Perform operations on the given ISession and IMessageProducer. + /// Perform operations on the given Session and MessageProducer. /// The message producer is not associated with any destination. /// - /// the NMS ISession object to use + /// the NMS Session object to use /// - /// the NMS IMessageProducer object to use + /// the NMS MessageProducer object to use /// - /// a result object from working with the ISession, if any (can be null) + /// a result object from working with the Session, if any (can be null) /// object DoInNms(ISession session, IMessageProducer producer); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/ISessionCallback.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/ISessionCallback.cs index dae030a3..f8c2e185 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/ISessionCallback.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/ISessionCallback.cs @@ -23,7 +23,7 @@ using Apache.NMS; namespace Spring.Messaging.Nms { /// Callback for executing any number of operations on a provided - /// ISession + /// Session /// /// ///

To be used with the NmsTemplate.Execute(ISessionCallback)} @@ -35,11 +35,11 @@ namespace Spring.Messaging.Nms public interface ISessionCallback { ///

Execute any number of operations against the supplied NMS - /// ISession, possibly returning a result. + /// Session, possibly returning a result. /// - /// the NMS ISession + /// the NMS Session /// - /// a result object from working with the ISession, if any (so can be null) + /// a result object from working with the Session, if any (so can be null) /// /// NMSException if there is any problem object DoInNms(ISession session); 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 78dff4f6..cd0733f7 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs @@ -20,6 +20,7 @@ using System; using Common.Logging; +using Spring.Messaging.Nms; using Spring.Messaging.Nms.Support; using Spring.Util; using Apache.NMS; @@ -28,7 +29,7 @@ namespace Spring.Messaging.Nms.Listener { /// /// Abstract base class for message listener containers. Can either host - /// a standard NMS or a Spring-specific + /// a standard NMS MessageListener or a Spring-specific /// /// public abstract class AbstractMessageListenerContainer : AbstractNmsListeningContainer @@ -61,6 +62,12 @@ namespace Spring.Messaging.Nms.Listener #region Properties + /// + /// Gets or sets the destination to receive messages from. Will be null + /// if the configured destination is not an actual Destination type; + /// c.f. when the destination is a String. + /// + /// The destination. public IDestination Destination { get @@ -80,6 +87,12 @@ namespace Spring.Messaging.Nms.Listener } + /// + /// Gets or sets the name of the destination to receive messages from. + /// Will be null if the configured destination is not a + /// string type; c.f. when it is an actual Destination object. + /// + /// The name of the destination. public string DestinationName { get @@ -95,6 +108,10 @@ namespace Spring.Messaging.Nms.Listener } + /// + /// Gets or sets the message selector. + /// + /// The message selector expression (or null if none).. public string MessageSelector { get { return messageSelector; } @@ -108,7 +125,7 @@ namespace Spring.Messaging.Nms.Listener /// /// /// - /// This can be either a standard NMS object or a + /// This can be either a standard NMS MessageListener object or a /// Spring object. /// /// @@ -132,6 +149,19 @@ namespace Spring.Messaging.Nms.Listener } + /// + /// Gets or sets a value indicating whether the subscription is durable. + /// + /// + /// Set whether to make the subscription durable. The durable subscription name + /// to be used can be specified through the "DurableSubscriptionName" property. + /// Default is "false". Set this to "true" to register a durable subscription, + /// typically in combination with a "DurableSubscriptionName" value (unless + /// your message listener class name is good enough as subscription name). + /// + /// Only makes sense when listening to a topic (pub-sub domain). + /// + /// true if the subscription is durable; otherwise, false. public bool SubscriptionDurable { get { return subscriptionDurable; } @@ -139,6 +169,18 @@ namespace Spring.Messaging.Nms.Listener } + /// + /// Gets or sets the name of the durable subscription to create. + /// + /// + /// To be applied in case of a topic (pub-sub domain) with subscription durability activated. + /// The durable subscription name needs to be unique within this client's + /// client id. Default is the class name of the specified message listener. + /// Note: Only 1 concurrent consumer (which is the default of this + /// message listener container) is allowed for each durable subscription. + /// + /// + /// The name of the durable subscription. public string DurableSubscriptionName { get @@ -153,6 +195,11 @@ namespace Spring.Messaging.Nms.Listener } + /// + /// Gets or sets the exception listener to notify in case of a NMSException thrown + /// by the registered message listener or the invocation infrastructure. + /// + /// The exception listener. public IExceptionListener ExceptionListener { get { return exceptionListener; } @@ -160,6 +207,24 @@ namespace Spring.Messaging.Nms.Listener } + /// + /// Gets or sets a value indicating whether to expose listener session to a registered + /// as well as to calls. + /// + /// + /// Default is "true", reusing the listener's Session. + /// Turn this off to expose a fresh Session fetched from the same + /// underlying Connection instead, which might be necessary + /// on some messaging providers. + /// Note that Sessions managed by an external transaction manager will + /// always get exposed to + /// calls. So in terms of NmsTemplate exposure, this setting only affects + /// locally transacted Sessions. + /// + /// + /// + /// true if expose listener session; otherwise, false. + /// public bool ExposeListenerSession { get { return exposeListenerISession; } @@ -196,18 +261,15 @@ namespace Spring.Messaging.Nms.Listener set { acceptMessagesWhileStopping = value; } } - public object LifecycleMonitor - { - get { return lifecycleMonitor; } - } - - - #endregion + /// + /// Validate that the destination is not null and that if the subscription is durable, then we are not + /// using the Pub/Sub domain. + /// protected override void ValidateConfiguration() { if (this.destination == null) @@ -284,7 +346,7 @@ namespace Spring.Messaging.Nms.Listener } /// - /// Invokes the specified listener: either as standard NMS IMessageListener + /// Invokes the specified listener: either as standard NMS MessageListener /// or (preferably) as Spring SessionAwareMessageListener. /// /// The session to operate on. @@ -384,17 +446,38 @@ namespace Spring.Messaging.Nms.Listener // Commit session or acknowledge message if (session.Transacted) { - if (SessionTransacted) + // Commit necessary - but avoid commit call is Session transaction is externally coordinated. + if (IsSessionLocallyTransacted(session)) { NmsUtils.CommitIfNecessary(session); } } - else if (ClientAcknowledge(session)) + else if (IsClientAcknowledge(session)) { message.Acknowledge(); } } + /// + /// Determines whether the given Session is locally transacted, that is, whether + /// its transaction is managed by this listener container's Session handling + /// and not by an external transaction coordinator. + /// + /// + /// The Session's own transacted flag will already have been checked + /// before. This method is about finding out whether the Session's transaction + /// is local or externally coordinated. + /// + /// The session to check. + /// + /// true if the is session locally transacted; otherwise, false. + /// + /// + protected virtual bool IsSessionLocallyTransacted(ISession session) + { + return SessionTransacted; + } + /// /// Perform a rollback, if appropriate. @@ -403,7 +486,7 @@ namespace Spring.Messaging.Nms.Listener /// In case of a rollback error protected virtual void RollbackIfNecessary(ISession session) { - if (session.Transacted && SessionTransacted) + if (session.Transacted && IsSessionLocallyTransacted(session)) { // Transacted session created by this container -> rollback NmsUtils.RollbackIfNecessary(session); @@ -419,7 +502,7 @@ namespace Spring.Messaging.Nms.Listener { try { - if (session.Transacted && SessionTransacted) + if (session.Transacted && IsSessionLocallyTransacted(session)) { // Transacted session created by this container -> rollback if (logger.IsDebugEnabled) @@ -428,7 +511,7 @@ namespace Spring.Messaging.Nms.Listener } NmsUtils.RollbackIfNecessary(session); } - } catch (NMSException ex2) + } catch (NMSException) { logger.Error("Application exception overriden by rollback exception", ex); throw; @@ -487,12 +570,19 @@ namespace Spring.Messaging.Nms.Listener #endregion - protected virtual void CheckMessageListener(System.Object messageListener) + /// + /// Checks the message listener, throwing an exception + /// if it does not correspond to a supported listener type. + /// By default, only a standard JMS MessageListener object or a + /// Spring object will be accepted. + /// + /// The message listener. + protected virtual void CheckMessageListener(object messageListener) { AssertUtils.ArgumentNotNull(messageListener, "IMessage Listener can not be null"); if (!(messageListener is IMessageListener || messageListener is ISessionAwareMessageListener)) { - throw new System.ArgumentException("messageListener needs to be of type [" + typeof(IMessageListener).FullName + "] or [" + typeof(ISessionAwareMessageListener).FullName + "]"); + throw new ArgumentException("messageListener needs to be of type [" + typeof(IMessageListener).FullName + "] or [" + typeof(ISessionAwareMessageListener).FullName + "]"); } } } diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractNmsListeningContainer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractNmsListeningContainer.cs index 92b05ac4..5f508693 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractNmsListeningContainer.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractNmsListeningContainer.cs @@ -22,7 +22,7 @@ using System; using Apache.NMS; using Common.Logging; using Spring.Context; -using Spring.Messaging.Nms.Connection; +using Spring.Messaging.Nms.Connections; using Spring.Messaging.Nms.Support; using Spring.Messaging.Nms.Support.IDestinations; using Spring.Objects.Factory; @@ -57,24 +57,39 @@ namespace Spring.Messaging.Nms.Listener private String clientId; - protected bool autoStartup = true; + private bool autoStartup = true; private string objectName; private IConnection sharedConnection; private bool sharedConnectionStarted = false; - + + /// + /// The monitor object to lock on when performing operations on the connection. + /// protected object sharedConnectionMonitor = new object(); private volatile bool active = false; private bool running = false; - + + /// + /// The monitor object to lock on when performing operations that update the lifecycle of the container. + /// protected object lifecycleMonitor = new object(); #endregion + /// + /// Gets or sets the client id for a shared Connection created and used by this container. + /// + /// + /// Note that client ids need to be unique among all active Connections + /// of the underlying JMS provider. Furthermore, a client id can only be + /// assigned if the original ConnectionFactory hasn't already assigned one. + /// + /// The client id. public string ClientId { set { clientId = value; } @@ -89,6 +104,18 @@ namespace Spring.Messaging.Nms.Listener set { this.autoStartup = value; } } + /// + /// Set the name of the object in the object factory that created this object. + /// + /// The name of the object in the factory. + /// + ///

+ /// Invoked after population of normal object properties but before an init + /// callback like 's + /// + /// method or a custom init-method. + ///

+ ///
public string ObjectName { set { objectName = value; } @@ -134,6 +161,11 @@ namespace Spring.Messaging.Nms.Listener return true; } } + /// + /// Gets a value indicating whether this container is currently active, + /// that is, whether it has been set up but not shut down yet. + /// + /// true if active; otherwise, false. public virtual bool Active { get @@ -146,11 +178,10 @@ namespace Spring.Messaging.Nms.Listener } - /// Return whether a shared NMS IConnection should be maintained + /// Return whether a shared NMS Connection should be maintained /// by this listener container base class. - /// - /// - /// + /// + /// protected abstract bool SharedConnectionEnabled { get; } /// @@ -180,7 +211,10 @@ namespace Spring.Messaging.Nms.Listener } } } - + + /// + /// Call base class method, then and then + /// public override void AfterPropertiesSet() { base.AfterPropertiesSet(); @@ -197,6 +231,9 @@ namespace Spring.Messaging.Nms.Listener } + /// + /// Calls when the application context destroys the container instance. + /// public void Dispose() { Shutdown(); @@ -237,6 +274,9 @@ namespace Spring.Messaging.Nms.Listener } } + /// + /// Stop the shared connection, call , and close this container. + /// public virtual void Shutdown() { logger.Debug("Shutting down message listener container"); @@ -283,6 +323,9 @@ namespace Spring.Messaging.Nms.Listener DoStart(); } + /// + /// Start the shared Connection, if any, and notify all invoker tasks. + /// protected virtual void DoStart() { // Lazily establish a shared Connection, if necessary. @@ -428,7 +471,7 @@ namespace Spring.Messaging.Nms.Listener { PrepareSharedConnection(con); return con; - } catch (NMSException ex) + } catch (NMSException) { NmsUtils.CloseConnection(con); throw; @@ -478,6 +521,10 @@ namespace Spring.Messaging.Nms.Listener } } + /// + /// Stops the shared connection. + /// + /// if thrown by NMS API methods. protected virtual void StopSharedConnection() { lock (this.sharedConnectionMonitor) @@ -504,7 +551,7 @@ namespace Spring.Messaging.Nms.Listener /// shared Connection failed. This is indicating to invokers that they need /// to establish the shared Connection themselves on first access. /// - public class SharedConnectionNotInitializedException : ApplicationException + public class SharedConnectionNotInitializedException : NMSException { /// /// Initializes a new instance of the class. diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/Adapter/MessageListenerAdapter.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/Adapter/MessageListenerAdapter.cs index a081f689..d83c2ffa 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/Adapter/MessageListenerAdapter.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/Adapter/MessageListenerAdapter.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using Common.Logging; using Spring.Expressions; +using Spring.Messaging.Nms.Listener; using Spring.Messaging.Nms.Support; using Spring.Messaging.Nms.Support.Converter; using Spring.Messaging.Nms.Support.IDestinations; @@ -10,6 +11,40 @@ using Apache.NMS; namespace Spring.Messaging.Nms.Listener.Adapter { + /// + /// Message listener adapter that delegates the handling of messages to target + /// listener methods via reflection, with flexible message type conversion. + /// Allows listener methods to operate on message content types, completely + /// independent from the NMS API. + /// + /// + /// By default, the content of incoming messages gets extracted before + /// being passed into the target listener method, to let the target method + /// operate on message content types such as String or byte array instead of + /// the raw Message. Message type conversion is delegated to a Spring + /// . By default, a + /// will be used. (If you do not want such automatic message conversion taking + /// place, then be sure to set the property + /// to null.) + /// + /// If a target listener method returns a non-null object (typically of a + /// message content type such as String or byte array), it will get + /// wrapped in a NMS Message and sent to the response destination + /// (either the NMS "reply-to" destination or the + /// specified. + /// + /// + /// The sending of response messages is only available when + /// using the entry point (typically through a + /// Spring message listener container). Usage as standard NMS MessageListener + /// does not support the generation of response messages. + /// + /// Consult the reference documentation for examples of method signatures compliant with this + /// adapter class. + /// + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) public class MessageListenerAdapter : IMessageListener { #region Logging @@ -18,9 +53,14 @@ namespace Spring.Messaging.Nms.Listener.Adapter #endregion - private object delegateObject; + /// + /// The default handler method name. + /// + public static string ORIGINAL_DEFAULT_HANDLER_METHOD = "HandleMessage"; - private string defaultListenerMethod = "HandleMessage"; + private object handlerObject; + + private string defaultHandlerMethod = ORIGINAL_DEFAULT_HANDLER_METHOD; private IExpression processingExpression; @@ -30,53 +70,107 @@ namespace Spring.Messaging.Nms.Listener.Adapter private IMessageConverter messageConverter; + /// + /// Initializes a new instance of the class with default settings. + /// public MessageListenerAdapter() { InitDefaultStrategies(); - delegateObject = this; - processingExpression = Spring.Expressions.Expression.Parse(defaultListenerMethod + "(#convertedObject)"); + handlerObject = this; + processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)"); } - public MessageListenerAdapter(object delegateObject) + /// + /// Initializes a new instance of the class for the given handler object + /// + /// The delegate object. + public MessageListenerAdapter(object handlerObject) { InitDefaultStrategies(); - this.delegateObject = delegateObject; + this.handlerObject = handlerObject; } - // TODO name change? - - public object DelegateObject + /// + /// Gets or sets the handler object to delegate message listening to. + /// + /// + /// Specified listener methods have to be present on this target object. + /// If no explicit handler object has been specified, listener + /// methods are expected to present on this adapter instance, that is, + /// on a custom subclass of this adapter, defining listener methods. + /// + /// The handler object. + public object HandlerObject { - get { return delegateObject; } - set { delegateObject = value; } + get { return handlerObject; } + set { handlerObject = value; } } - public string DefaultListenerMethod + /// + /// Gets or sets the default handler method to delegate to, + /// for the case where no specific listener method has been determined. + /// Out-of-the-box value is ("HandleMessage"}. + /// + /// The default handler method. + public string DefaultHandlerMethod { - get { return defaultListenerMethod; } + get { return defaultHandlerMethod; } set { - defaultListenerMethod = value; + defaultHandlerMethod = value; } } + /// + /// Sets the default destination to send response messages to. This will be applied + /// in case of a request message that does not carry a "JMSReplyTo" field. + /// Response destinations are only relevant for listener methods that return + /// result objects, which will be wrapped in a response message and sent to a + /// response destination. + /// + /// Alternatively, specify a "DefaultResponseQueueName" or "DefaultResponseTopicName", + /// to be dynamically resolved via the DestinationResolver. + /// + /// + /// The default response destination. public object DefaultResponseDestination { set { defaultResponseDestination = value; } } + /// + /// Sets the name of the default response queue to send response messages to. + /// This will be applied in case of a request message that does not carry a + /// "NMSReplyTo" field. + /// Alternatively, specify a JMS Destination object as "defaultResponseDestination". + /// + /// The name of the default response destination queue. public string DefaultResponseDestinationQueueName { set { defaultResponseDestination = new DestinationNameHolder(value, false); } } + /// + /// Sets the name of the default response topic to send response messages to. + /// This will be applied in case of a request message that does not carry a + /// "NMSReplyTo" field. + /// Alternatively, specify a JMS Destination object as "defaultResponseDestination". + /// + /// The name of the default response destination topic. public string DefaultResponseDestinationTopicName { set { defaultResponseDestination = new DestinationNameHolder(value, true); } } + /// + /// Gets or sets the destination resolver that should be used to resolve response + /// destination names for this adapter. + /// The default resolver is a . + /// Specify another implementation, for other strategies, perhaps from a directory service. + /// + /// The destination resolver. public IDestinationResolver DestinationResolver { get { return destinationResolver; } @@ -87,28 +181,39 @@ namespace Spring.Messaging.Nms.Listener.Adapter } } + /// + /// Gets or sets the message converter that will convert incoming JMS messages to + /// listener method arguments, and objects returned from listener + /// methods back to NMS messages. + /// + /// + /// The default converter is a {@link SimpleMessageConverter}, which is able + /// to handle BytesMessages}, TextMessages, MapMessages, and ObjectMessages. + /// + /// + /// The message converter. public IMessageConverter MessageConverter { get { return messageConverter; } set { messageConverter = value; } } - private void InitDefaultStrategies() - { - MessageConverter = new SimpleMessageConverter(); - } - - protected virtual void HandleListenerException(Exception e) - { - logger.Error("Listener execution failed", e); - } - - protected virtual string GetListenerMethodName(IMessage originalIMessage, object extractedMessage) - { - return DefaultListenerMethod; - } - + /// + /// Standard JMS {@link MessageListener} entry point. + /// Delegates the message to the target listener method, with appropriate + /// conversion of the message arguments + /// + /// + /// + /// In case of an exception, the method will be invoked. + /// Note + /// Does not support sending response messages based on + /// result objects returned from listener methods. Use the + /// entry point (typically through a Spring + /// message listener container) for handling result objects as well. + /// + /// The incoming message. public void OnMessage(IMessage message) { try @@ -121,9 +226,19 @@ namespace Spring.Messaging.Nms.Listener.Adapter } } + /// + /// Spring entry point. + /// + /// Delegates the message to the target listener method, with appropriate + /// conversion of the message argument. If the target method returns a + /// non-null object, wrap in a NMS message and send it back. + /// + /// + /// The incoming message. + /// The session to operate on. public void OnMessage(IMessage message, ISession session) { - object convertedMessage = ExtractIMessage(message); + object convertedMessage = ExtractMessage(message); IDictionary vars = new Hashtable(); @@ -131,11 +246,11 @@ namespace Spring.Messaging.Nms.Listener.Adapter //Need to parse each time since have overloaded methods and //expression processor caches target of first invocation. - //TODO - use regular reflection. - processingExpression = Expression.Parse(defaultListenerMethod + "(#convertedObject)"); + //TODO - check JIRA as I believe this has been fixed, otherwise, use regular reflection. -MLP + processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)"); //Invoke message handler method and get result. - object result = processingExpression.GetValue(delegateObject, vars); + object result = processingExpression.GetValue(handlerObject, vars); if (result != null) { HandleResult(result, message, session); @@ -146,7 +261,68 @@ namespace Spring.Messaging.Nms.Listener.Adapter } } - private void HandleResult(object result, IMessage request, ISession session) + /// + /// Initialize the default implementations for the adapter's strategies. + /// + protected virtual void InitDefaultStrategies() + { + MessageConverter = new SimpleMessageConverter(); + } + + /// + /// Handle the given exception that arose during listener execution. + /// The default implementation logs the exception at error level. + /// This method only applies when used as standard NMS MessageListener. + /// In case of the Spring mechanism, + /// exceptions get handled by the caller instead. + /// + /// + /// The exception to handle. + protected virtual void HandleListenerException(Exception ex) + { + logger.Error("Listener execution failed", ex); + } + + /// + /// Extract the message body from the given message. + /// + /// The message. + /// the content of the message, to be passed into the + /// listener method as argument + /// if thrown by NMS API methods + private object ExtractMessage(IMessage message) + { + IMessageConverter converter = MessageConverter; + if (converter != null) + { + return converter.FromMessage(message); + } + return message; + } + + /// + /// Gets the name of the listener method that is supposed to + /// handle the given message. + /// The default implementation simply returns the configured + /// default listener method, if any. + /// + /// The NMS request message. + /// The converted JMS request message, + /// to be passed into the listener method as argument. + /// the name of the listener method (never null) + /// if thrown by NMS API methods + protected virtual string GetHandlerMethodName(IMessage originalIMessage, object extractedMessage) + { + return DefaultHandlerMethod; + } + + /// + /// Handles the given result object returned from the listener method, sending a response message back. + /// + /// The result object to handle (never null). + /// The original request message. + /// The session to operate on (may be null). + protected virtual void HandleResult(object result, IMessage request, ISession session) { if (session != null) { @@ -170,6 +346,14 @@ namespace Spring.Messaging.Nms.Listener.Adapter } } + /// + /// Builds a JMS message to be sent as response based on the given result object. + /// + /// The JMS Session to operate on. + /// The content of the message, as returned from the listener method. + /// the JMS Message (never null) + /// If there was an error in message conversion + /// if thrown by NMS API methods protected virtual IMessage BuildMessage(ISession session, Object result) { IMessageConverter converter = MessageConverter; @@ -182,18 +366,43 @@ namespace Spring.Messaging.Nms.Listener.Adapter IMessage msg = result as IMessage; if (msg == null) { - throw new IMessageConversionException( + throw new MessageConversionException( "No IMessageConverter specified - cannot handle message [" + result + "]"); } return msg; } } + /// + /// Post-process the given response message before it will be sent. The default implementation + /// sets the response's correlation id to the request message's correlation id. + /// + /// The original incoming message. + /// The outgoing JMS message about to be sent. + /// if thrown by NMS API methods protected virtual void PostProcessResponse(IMessage request, IMessage response) { response.NMSCorrelationID = request.NMSCorrelationID; } - + + /// + /// Determine a response destination for the given message. + /// + /// + /// The default implementation first checks the JMS Reply-To + /// Destination of the supplied request; if that is not null + /// it is returned; if it is null, then the configured + /// default response destination} + /// is returned; if this too is null, then an + /// is thrown. + /// + /// + /// The original incoming message. + /// Tthe outgoing message about to be sent. + /// The session to operate on. + /// the response destination (never null) + /// if thrown by NMS API methods + /// if no destination can be determined. protected virtual IDestination GetResponseDestination(IMessage request, IMessage response, ISession session) { IDestination replyTo = request.NMSReplyTo; @@ -208,7 +417,13 @@ namespace Spring.Messaging.Nms.Listener.Adapter } return replyTo; } - + + /// + /// Resolves the default response destination into a Destination, using this + /// accessor's in case of a destination name. + /// + /// The session to operate on. + /// The located destination protected virtual IDestination ResolveDefaultResponseDestination(ISession session) { IDestination dest = defaultResponseDestination as IDestination; @@ -225,7 +440,13 @@ namespace Spring.Messaging.Nms.Listener.Adapter return null; } - + + /// + /// Sends the given response message to the given destination. + /// + /// The session to operate on. + /// The destination to send to. + /// The outgoing message about to be sent. protected virtual void SendResponse(ISession session, IDestination destination, IMessage response) { IMessageProducer producer = session.CreateProducer(destination); @@ -239,24 +460,22 @@ namespace Spring.Messaging.Nms.Listener.Adapter NmsUtils.CloseMessageProducer(producer); } } - + + /// + /// Post-process the given message producer before using it to send the response. + /// The default implementation is empty. + /// + /// The producer that will be used to send the message. + /// The outgoing message about to be sent. protected virtual void PostProcessProducer(IMessageProducer producer, IMessage response) { } - - - private object ExtractIMessage(IMessage message) - { - IMessageConverter converter = MessageConverter; - if (converter != null) - { - return converter.FromMessage(message); - } - return message; - } } + /// + /// Internal class combining a destination name and its target destination type (queue or topic). + /// internal class DestinationNameHolder { private string name; diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/DefaultMessageListenerContainer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/DefaultMessageListenerContainer.cs deleted file mode 100644 index f952d7f4..00000000 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/DefaultMessageListenerContainer.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Spring.Messaging.Nms.Listener -{ - public class DefaultMessageListenerContainer - { - } -} diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/ISessionAwareMessageListener.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/ISessionAwareMessageListener.cs index 0b1b61c8..eb2cc30b 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/ISessionAwareMessageListener.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/ISessionAwareMessageListener.cs @@ -1,17 +1,49 @@ +#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.Listener { + /// + /// Variant of the standard NMS MessageListener interface, + /// offering not only the received Message but also the underlying + /// Session object. The latter can be used to send reply messages, + /// without the need to access an external Connection/Session, + /// i.e. without the need to access the underlying ConnectionFactory. + /// + /// + /// Supported by Spring's + /// as direct alternative to the standard MessageListener interface. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) public interface ISessionAwareMessageListener { - /// Callback for processing a received NMS message. - /// Implementors are supposed to process the given IMessage, - /// typically sending reply messages through the given ISession. + /// Implementors are supposed to process the given Message, + /// typically sending reply messages through the given Session. /// /// the received NMS message /// - /// the underlying NMS ISession + /// the underlying NMS Session /// /// NMSException if thrown by NMS methods void OnMessage(IMessage message, ISession session); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/LocallyExposedNmsResourceHolder.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/LocallyExposedNmsResourceHolder.cs index 16cb59fc..1eda9a3a 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/LocallyExposedNmsResourceHolder.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/LocallyExposedNmsResourceHolder.cs @@ -19,7 +19,7 @@ #endregion using Apache.NMS; -using Spring.Messaging.Nms.Connection; +using Spring.Messaging.Nms.Connections; namespace Spring.Messaging.Nms.Listener { 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 50f10c07..500a2ef9 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/SimpleMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/SimpleMessageListenerContainer.cs @@ -21,6 +21,7 @@ using System; using Common.Logging; using Spring.Collections; +using Spring.Messaging.Nms; using Spring.Messaging.Nms.Support; using Apache.NMS; using Spring.Transaction.Support; @@ -30,7 +31,7 @@ namespace Spring.Messaging.Nms.Listener { /// /// Message listener container that uses the plain NMS client API's - /// method to create concurrent + /// MessageConsumer.Listener method to create concurrent /// MessageConsumers for the specified listeners. /// public class SimpleMessageListenerContainer : AbstractMessageListenerContainer, IExceptionListener @@ -57,12 +58,31 @@ namespace Spring.Messaging.Nms.Listener #region Properties + /// + /// Gets or sets a value indicating whether to inhibit the delivery of messages published by its own connection. + /// Default is "false". + /// + /// true if should inhibit the delivery of messages published by its own connection; otherwise, false. public bool PubSubNoLocal { get { return pubSubNoLocal; } set { pubSubNoLocal = value; } } + /// + /// Specify the number of concurrent consumers to create. Default is 1. + /// + /// + /// Raising the number of concurrent consumers is recommendable in order + /// to scale the consumption of messages coming in from a queue. However, + /// note that any ordering guarantees are lost once multiple consumers are + /// registered. In general, stick with 1 consumer for low-volume queues. + /// Do not raise the number of concurrent consumers for a topic. + /// This would lead to concurrent consumption of the same message, + /// which is hardly ever desirable. + /// + /// + /// The concurrent consumers. public int ConcurrentConsumers { set @@ -82,6 +102,10 @@ namespace Spring.Messaging.Nms.Listener #endregion + /// + /// Call base class for valdation and then check that if the subscription is durable that the number of + /// concurrent consumers is equal to one. + /// protected override void ValidateConfiguration() { base.ValidateConfiguration(); @@ -120,10 +144,18 @@ namespace Spring.Messaging.Nms.Listener { base.PrepareSharedConnection(connection); connection.ExceptionListener += OnException; - } + } + + /// + /// implementation, invoked by the NMS provider in + /// case of connection failures. Re-initializes this listener container's + /// shared connection and its sessions and consumers. + /// + /// The reported connection exception. public void OnException(Exception exception) { + // First invoke the user-specific ExceptionListener, if any. InvokeExceptionListener(exception); // now try to recover the shared Connection and all consumers... if (logger.IsInfoEnabled) @@ -178,7 +210,7 @@ namespace Spring.Messaging.Nms.Listener /// registering a MessageListener for the specified listener /// /// The session to work on. - /// the IMessageConsumer"/> + /// the MessageConsumer"/> /// if thrown by NMS methods private IMessageConsumer CreateListenerConsumer(ISession session) { @@ -216,6 +248,12 @@ namespace Spring.Messaging.Nms.Listener } + /// + /// Creates a MessageConsumer for the given Session and Destination. + /// + /// The session to create a MessageConsumer for. + /// The destination to create a MessageConsumer for. + /// The new MessageConsumer protected IMessageConsumer CreateConsumer(ISession session, IDestination destination) { // Only pass in the NoLocal flag in case of a Topic: diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/MessageCreatorDelegate.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/MessageCreatorDelegate.cs index 23090f01..1e87ccf4 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/MessageCreatorDelegate.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/MessageCreatorDelegate.cs @@ -25,10 +25,10 @@ namespace Spring.Messaging.Nms /// /// Delegate that creates a NMS message given a ISession /// - /// the NMS ISession to be used to create the - /// IMessage (never null) + /// the NMS Session to be used to create the + /// Message (never null) /// - /// the IMessage to be sent + /// the Message to be sent /// /// NMSException if thrown by NMS API methods public delegate IMessage IMessageCreatorDelegate(ISession session); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsGatewaySupport.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsGatewaySupport.cs index 386e53de..4ce57abe 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsGatewaySupport.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsGatewaySupport.cs @@ -29,9 +29,9 @@ namespace Spring.Messaging.Nms /// Convenient super class for application classes that need NMS access. ///
/// - /// Requires a IConnectionFactory or a NmsTemplate instance to be set. - /// It will create its own NmsTemplate if a IConnectionFactory is passed in. - /// A custom NmsTemplate instance can be created for a given IConnectionFactory + /// 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. /// /// @@ -40,7 +40,7 @@ namespace Spring.Messaging.Nms #region Logging - protected readonly ILog logger = LogManager.GetLogger(typeof(NmsGatewaySupport)); + private readonly ILog logger = LogManager.GetLogger(typeof(NmsGatewaySupport)); #endregion @@ -59,7 +59,7 @@ namespace Spring.Messaging.Nms /// /// Gets or sets he NMS connection factory to be used by the gateway. - /// Will automatically create a NmsTemplate for the given IConnectionFactory. + /// Will automatically create a NmsTemplate for the given ConnectionFactory. /// /// The connection factory. public IConnectionFactory ConnectionFactory @@ -75,9 +75,9 @@ namespace Spring.Messaging.Nms } /// - /// Creates a NmsTemplate for the given IConnectionFactory. + /// Creates a NmsTemplate for the given ConnectionFactory. /// - /// Only invoked if populating the gateway with a IConnectionFactory reference. + /// Only invoked if populating the gateway with a ConnectionFactory reference. /// Can be overridden in subclasses to provide a different NmsTemplate instance /// /// @@ -88,6 +88,9 @@ namespace Spring.Messaging.Nms return new NmsTemplate(connectionFactory); } + /// + /// Ensures that the JmsTemplate is specified and calls . + /// public void AfterPropertiesSet() { if (jmsTemplate == null) diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs index 35c6e712..41c39beb 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs @@ -20,7 +20,7 @@ using System; using Common.Logging; -using Spring.Messaging.Nms.Connection; +using Spring.Messaging.Nms.Connections; using Spring.Messaging.Nms.Support; using Spring.Messaging.Nms.Support.Converter; using Spring.Messaging.Nms.Support.IDestinations; @@ -37,7 +37,7 @@ namespace Spring.Messaging.Nms /// For other operations, this is not necessary. /// Point-to-Point (Queues) is the default domain. /// - /// Default settings for NMS ISessions are "not transacted" and "auto-acknowledge". + /// Default settings for NMS Sessions is "auto-acknowledge". /// /// This template uses a DynamicDestinationResolver and a SimpleMessageConverter /// as default strategies for resolving a destination name or converting a message, @@ -57,12 +57,14 @@ namespace Spring.Messaging.Nms #endregion #region Fields + /// + /// Timeout value indicating that a receive operation should + /// check if a message is immediately available without blocking. + /// public static readonly long DEFAULT_RECEIVE_TIMEOUT = -1; - private NmsTemplateResourceFactory transactionalResourceFactory; - private object defaultDestination; private IMessageConverter messageConverter; @@ -92,9 +94,9 @@ namespace Spring.Messaging.Nms /// Create a new NmsTemplate. /// - /// Note: The IConnectionFactory has to be set before using the instance. + /// Note: The ConnectionFactory has to be set before using the instance. /// This constructor can be used to prepare a NmsTemplate via an ObjectFactory, - /// typically setting the IConnectionFactory. + /// typically setting the ConnectionFactory. /// public NmsTemplate() { @@ -103,8 +105,8 @@ namespace Spring.Messaging.Nms } - /// Create a new NmsTemplate, given a IConnectionFactory. - /// the IConnectionFactory to obtain IConnections from + /// Create a new NmsTemplate, given a ConnectionFactory. + /// the ConnectionFactory to obtain IConnections from /// public NmsTemplate(IConnectionFactory connectionFactory) : this() @@ -134,6 +136,7 @@ namespace Spring.Messaging.Nms } } + private void CheckMessageConverter() { if (MessageConverter == null) @@ -143,12 +146,12 @@ namespace Spring.Messaging.Nms } /// Execute the action specified by the given action object within a - /// NMS ISession. + /// NMS Session. /// /// Generalized version of execute(ISessionCallback), - /// allowing the NMS IConnection to be started on the fly. + /// allowing the NMS Connection to be started on the fly. ///

Use execute(ISessionCallback) for the general case. - /// Starting the NMS IConnection is just necessary for receiving messages, + /// Starting the NMS Connection is just necessary for receiving messages, /// which is preferably achieved through the receive methods.

///
/// callback object that exposes the session @@ -167,7 +170,8 @@ namespace Spring.Messaging.Nms try { ISession sessionToUse = - ConnectionFactoryUtils.DoGetTransactionalSession(ConnectionFactory, transactionalResourceFactory); + ConnectionFactoryUtils.DoGetTransactionalSession(ConnectionFactory, transactionalResourceFactory, + startConnection); if (sessionToUse == null) { conToClose = CreateConnection(); @@ -184,11 +188,10 @@ namespace Spring.Messaging.Nms } return action.DoInNms(sessionToUse); } - //TODO make sure don't want to do exception translation. finally { NmsUtils.CloseSession(sessionToClose); - ConnectionFactoryUtils.ReleaseConnection(conToClose, ConnectionFactory, startConnection); + ConnectionFactoryUtils.ReleaseConnection(conToClose, ConnectionFactory, startConnection); } } @@ -245,12 +248,10 @@ namespace Spring.Messaging.Nms set { messageConverter = value; } } - - //TODO check tibco support for message id...prob yes.. /// - /// Gets or sets a value indicating whether IMessageIds are. + /// Gets or sets a value indicating whether Message Ids are enabled. /// - /// true if [message id enabled]; otherwise, false. + /// true if message id enabled; otherwise, false. virtual public bool MessageIdEnabled { get { return messageIdEnabled; } @@ -258,7 +259,6 @@ namespace Spring.Messaging.Nms set { messageIdEnabled = value; } } - //TODO check tibco support for message id...prob yes.., so don't really need it then. /// /// Gets or sets a value indicating whether message timestamps are enabled. /// @@ -326,9 +326,6 @@ namespace Spring.Messaging.Nms set { persistent = value; } } - - //TODO verify admin... - /// /// Gets or sets the priority when sending. /// @@ -358,6 +355,11 @@ namespace Spring.Messaging.Nms #endregion + /// + /// Extract the content from the given JMS message. + /// + /// The Message to convert (can be null). + /// The content of the message, or null if none protected virtual object DoConvertFromMessage(IMessage message) { if (message != null) @@ -369,7 +371,7 @@ namespace Spring.Messaging.Nms #region NMS Factory Methods - /// Fetch an appropriate IConnection from the given NmsResourceHolder. + /// Fetch an appropriate Connection from the given NmsResourceHolder. /// /// the NmsResourceHolder /// @@ -381,7 +383,7 @@ namespace Spring.Messaging.Nms return holder.GetConnection(); } - /// Fetch an appropriate ISession from the given NmsResourceHolder. + /// Fetch an appropriate Session from the given NmsResourceHolder. /// /// the NmsResourceHolder /// @@ -393,16 +395,16 @@ namespace Spring.Messaging.Nms return holder.GetSession(); } - /// Create a NMS IMessageProducer for the given ISession and IDestination, + /// Create a NMS MessageProducer for the given Session and Destination, /// configuring it to disable message ids and/or timestamps (if necessary). ///

Delegates to doCreateProducer for creation of the raw - /// NMS IMessageProducer, which needs to be specific to NMS 1.1 or 1.0.2.

+ /// NMS MessageProducer

///
- /// the NMS ISession to create a IMessageProducer for + /// the NMS Session to create a MessageProducer for /// - /// the NMS IDestination to create a IMessageProducer for + /// the NMS Destination to create a MessageProducer for /// - /// the new NMS IMessageProducer + /// the new NMS MessageProducer /// /// NMSException if thrown by NMS API methods /// @@ -426,14 +428,33 @@ namespace Spring.Messaging.Nms } - /// Create a raw NMS IMessageProducer for the given ISession and IDestination. - ///

This implementation uses NMS 1.1 API.

+ /// + /// Determines whether the given Session is locally transacted, that is, whether + /// its transaction is managed by this template class's Session handling + /// and not by an external transaction coordinator. /// - /// the NMS ISession to create a IMessageProducer for + /// + /// The Session's own transacted flag will already have been checked + /// before. This method is about finding out whether the Session's transaction + /// is local or externally coordinated. + /// + /// The session to check. + /// + /// true if the session is locally transacted; otherwise, false. + /// + protected virtual bool IsSessionLocallyTransacted(ISession session) + { + return SessionTransacted && + !ConnectionFactoryUtils.IsSessionTransactional(session, ConnectionFactory); + } + + /// Create a raw NMS MessageProducer for the given Session and Destination. + /// + /// the NMS Session to create a MessageProducer for /// - /// the NMS IDestination to create a IMessageProducer for + /// the NMS IDestination to create a MessageProducer for /// - /// the new NMS IMessageProducer + /// the new NMS MessageProducer /// /// NMSException if thrown by NMS API methods protected virtual IMessageProducer DoCreateProducer(ISession session, IDestination destination) @@ -441,12 +462,11 @@ namespace Spring.Messaging.Nms return session.CreateProducer(destination); } - /// Create a NMS IMessageConsumer for the given ISession and IDestination. - ///

This implementation uses NMS 1.1 API.

+ /// Create a NMS MessageConsumer for the given Session and Destination. /// - /// the NMS ISession to create a IMessageConsumer for + /// the NMS Session to create a MessageConsumer for /// - /// the NMS IDestination to create a IMessageConsumer for + /// the NMS Destination to create a MessageConsumer for /// /// the message selector for this consumer (can be null) /// @@ -469,14 +489,24 @@ namespace Spring.Messaging.Nms } } - //TODO refactor to not pass null as a 'switch' for behavior. - + /// + /// Send the given message. + /// + /// 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) { AssertUtils.ArgumentNotNull(messageCreatorDelegate, "IMessageCreatorDelegate must not be null"); DoSend(session, destination, null, messageCreatorDelegate); } + /// + /// Send the given message. + /// + /// The session to operate on. + /// The destination to send to. + /// The message creator callback to create a Message. protected internal virtual void DoSend(ISession session, IDestination destination, IMessageCreator messageCreator) { AssertUtils.ArgumentNotNull(messageCreator, "IMessageCreator must not be null"); @@ -484,13 +514,13 @@ namespace Spring.Messaging.Nms } /// Send the given NMS message. - /// the NMS ISession to operate on + /// the NMS Session to operate on /// - /// the NMS IDestination to send to + /// the NMS Destination to send to /// - /// callback to create a NMS IMessage + /// callback to create a NMS Message /// - /// delegate callback to create a NMS IMessage + /// delegate callback to create a NMS Message /// /// NMSException if thrown by NMS API methods protected internal virtual void DoSend(ISession session, IDestination destination, IMessageCreator messageCreator, @@ -516,8 +546,8 @@ namespace Spring.Messaging.Nms } DoSend(producer, message); - // Check commit - avoid commit call within a JTA transaction. - if (session.Transacted && !TransactionSynchronizationManager.HasResource(ConnectionFactory)) + // Check commit, avoid commit call is Session transaction is externally coordinated. + if (session.Transacted && IsSessionLocallyTransacted(session)) { // Transacted session created by this template -> commit. NmsUtils.CommitIfNecessary(session); @@ -531,9 +561,9 @@ namespace Spring.Messaging.Nms /// Actually send the given NMS message. - /// the NMS IMessageProducer to send with + /// the NMS MessageProducer to send with /// - /// the NMS IMessage to send + /// the NMS Message to send /// /// NMSException if thrown by NMS API methods protected virtual void DoSend(IMessageProducer producer, IMessage message) @@ -554,10 +584,10 @@ namespace Spring.Messaging.Nms #region INmsOperations Implementation /// Execute the action specified by the given action object within - /// a NMS ISession. - ///

Note: The value of isPubSubDomain affects the behavior of this method. - /// If isPubSubDomain equals true, then a ISession is passed to the callback. - /// If false, then a ISession is passed to the callback.

+ /// a NMS Session. + ///

Note: 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 Session is passed to the callback.

///
/// callback object that exposes the session /// @@ -570,7 +600,7 @@ namespace Spring.Messaging.Nms } /// Send a message to a NMS destination. The callback gives access to - /// the NMS session and IMessageProducer in order to do more complex + /// the NMS session and MessageProducer in order to do more complex /// send operations. /// /// callback object that exposes the session/producer pair @@ -603,7 +633,7 @@ namespace Spring.Messaging.Nms } /// Send a message to the specified destination. - /// The IMessageCreator callback creates the message given a ISession. + /// The MessageCreator callback creates the message given a Session. /// /// the destination to send this message to /// @@ -616,7 +646,7 @@ namespace Spring.Messaging.Nms } /// Send a message to the specified destination. - /// The IMessageCreator callback creates the message given a ISession. + /// The MessageCreator callback creates the message given a Session. /// /// the name of the destination to send this message to /// (to be resolved to an actual destination by a DestinationResolver) @@ -649,7 +679,7 @@ namespace Spring.Messaging.Nms } /// Send a message to the specified destination. - /// The IMessageCreator callback creates the message given a ISession. + /// The MessageCreator callback creates the message given a Session. /// /// the destination to send this message to /// @@ -663,7 +693,7 @@ namespace Spring.Messaging.Nms } /// Send a message to the specified destination. - /// The IMessageCreator callback creates the message given a ISession. + /// The MessageCreator callback creates the message given a Session. /// /// the destination to send this message to /// @@ -898,12 +928,25 @@ namespace Spring.Messaging.Nms return Execute(new ReceiveSelectedCallback(this, destinationName, messageSelector), true) as IMessage; } - + + /// + /// Receive a message. + /// + /// The session to operate on. + /// The destination to receive from. + /// The message selector for this consumer (can be null + /// The Message received, or null if none. protected virtual IMessage DoReceive(ISession session, IDestination destination, string messageSelector) { return DoReceive(session, CreateConsumer(session, destination, messageSelector)); } - + + /// + /// Receive a message. + /// + /// The session to operate on. + /// The consumer to receive with. + /// The Message received, or null if none protected virtual IMessage DoReceive(ISession session, IMessageConsumer consumer) { try @@ -920,14 +963,14 @@ namespace Spring.Messaging.Nms : consumer.Receive(); if (session.Transacted) { - // Commit necessary - but avoid commit call within a JTA transaction. - if (resourceHolder == null) + // Commit necessary - but avoid commit call is Session transaction is externally coordinated. + if (IsSessionLocallyTransacted(session)) { // Transacted session created by this template -> commit. NmsUtils.CommitIfNecessary(session); } } - else if (ClientAcknowledge(session)) + else if (IsClientAcknowledge(session)) { // Manually acknowledge message, if any. if (message != null) @@ -1060,6 +1103,9 @@ namespace Spring.Messaging.Nms #region Supporting Internal Classes + /// + /// ResourceFactory implementation that delegates to this template's callback methods. + /// private class NmsTemplateResourceFactory : ConnectionFactoryUtils.ResourceFactory { private NmsTemplate enclosingTemplateInstance; @@ -1074,34 +1120,34 @@ namespace Spring.Messaging.Nms enclosingTemplateInstance = enclosingInstance; } - public NmsTemplate Enclosing_Instance + public NmsTemplate EnclosingInstance { get { return enclosingTemplateInstance; } } public virtual IConnection GetConnection(NmsResourceHolder holder) { - return Enclosing_Instance.GetConnection(holder); + return EnclosingInstance.GetConnection(holder); } public virtual ISession GetSession(NmsResourceHolder holder) { - return Enclosing_Instance.GetSession(holder); + return EnclosingInstance.GetSession(holder); } public virtual IConnection CreateConnection() { - return Enclosing_Instance.CreateConnection(); + return EnclosingInstance.CreateConnection(); } public virtual ISession CreateSession(IConnection con) { - return Enclosing_Instance.CreateSession(con); + return EnclosingInstance.CreateSession(con); } public bool SynchedLocalTransactionAllowed { - get { return Enclosing_Instance.SessionTransacted; } + get { return EnclosingInstance.SessionTransacted; } } } diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/IMessageConverter.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/IMessageConverter.cs index 214119e0..23368542 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/IMessageConverter.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/IMessageConverter.cs @@ -31,25 +31,25 @@ namespace Spring.Messaging.Nms.Support.Converter /// Mark Pollack (.NET) public interface IMessageConverter { - /// Convert a .NET object to a NMS IMessage using the supplied session + /// Convert a .NET object to a NMS Message using the supplied session /// to create the message object. /// /// the object to convert /// - /// the ISession to use for creating a NMS IMessage + /// the Session to use for creating a NMS Message /// - /// the NMS IMessage + /// the NMS Message /// /// NMSException if thrown by NMS API methods - /// IMessageConversionException in case of conversion failure + /// MessageConversionException in case of conversion failure IMessage ToMessage(object objectToConvert, ISession session); - /// Convert from a NMS IMessage to a .NET object. + /// Convert from a NMS Message to a .NET object. /// the message to convert /// /// the converted .NET object /// - /// IMessageConversionException in case of conversion failure + /// MessageConversionException in case of conversion failure object FromMessage(IMessage message); } } diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/MessageConversionException.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/MessageConversionException.cs index c7fbca9f..8c1f1fd9 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/MessageConversionException.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Converter/MessageConversionException.cs @@ -19,24 +19,18 @@ #endregion using System; -using System.Runtime.Serialization; +using Apache.NMS; namespace Spring.Messaging.Nms.Support.Converter { /// Thrown by IMessageConverter implementations when the conversion - /// of an object to/from a IMessage fails. - /// + /// of an object to/from a Message fails. /// /// Mark Pollack - [Serializable] - public class IMessageConversionException : ApplicationException + public class MessageConversionException : NMSException { - //TODO add jms root exception hierarchy?.... #region Constructor (s) / Destructor - /// Creates a new instance of the IMessageConverterException class. - public IMessageConversionException() - { - } + /// /// Creates a new instance of the IMessageConverterException class. with the specified message. @@ -44,7 +38,7 @@ namespace Spring.Messaging.Nms.Support.Converter /// /// A message about the exception. /// - public IMessageConversionException(string message) + public MessageConversionException(string message) : base(message) { } @@ -59,27 +53,11 @@ namespace Spring.Messaging.Nms.Support.Converter /// /// The root exception that is being wrapped. /// - public IMessageConversionException(string message, Exception rootCause) + public MessageConversionException(string message, Exception rootCause) : base(message, rootCause) { } - /// - /// Creates a new instance of the IMessageConverterException class. - /// - /// - /// The - /// that holds the serialized object data about the exception being thrown. - /// - /// - /// The - /// that contains contextual information about the source or destination. - /// - protected IMessageConversionException( - SerializationInfo info, StreamingContext context) - : base(info, context) - { - } #endregion } } 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 11fd51b4..f3acf628 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 @@ -39,17 +39,17 @@ namespace Spring.Messaging.Nms.Support.Converter /// Mark Pollack (.NET) public class SimpleMessageConverter : IMessageConverter { - /// Convert a .NET object to a NMS IMessage using the supplied session + /// Convert a .NET object to a NMS Message using the supplied session /// to create the message object. /// /// the object to convert /// - /// the ISession to use for creating a NMS IMessage + /// the Session to use for creating a NMS Message /// - /// the NMS IMessage + /// the NMS Message /// /// NMSException if thrown by NMS API methods - /// IMessageConversionException in case of conversion failure + /// MessageConversionException in case of conversion failure public IMessage ToMessage(object objectToConvert, ISession session) { if (objectToConvert is IMessage) @@ -75,16 +75,16 @@ namespace Spring.Messaging.Nms.Support.Converter } else { - throw new IMessageConversionException("Cannot convert object [" + objectToConvert + "] to NMS message"); + throw new MessageConversionException("Cannot convert object [" + objectToConvert + "] to NMS message"); } } - /// Convert from a NMS IMessage to a .NET object. + /// Convert from a NMS Message to a .NET object. /// the message to convert /// /// the converted .NET object /// - /// IMessageConversionException in case of conversion failure + /// MessageConversionException in case of conversion failure public object FromMessage(IMessage message) { if (message is ITextMessage) @@ -155,7 +155,7 @@ namespace Spring.Messaging.Nms.Support.Converter if (!(entry.Key is string)) { //UPGRADE_TODO: The equivalent in .NET for method 'java.lang.Class.getName' may return a different value. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1043'" - throw new IMessageConversionException("Cannot convert non-String key of type [" + + throw new MessageConversionException("Cannot convert non-String key of type [" + (entry.Key != null ? entry.Key.GetType().FullName : null) + "] to IMapMessage entry"); } @@ -209,7 +209,7 @@ namespace Spring.Messaging.Nms.Support.Converter /// /// the resulting Map /// - /// NMSException if thrown by NMS methods + /// NMSException if thrown by NMS methods protected virtual IDictionary ExtractMapFromMessage(IMapMessage message) { IDictionary dictionary = new Hashtable(); @@ -222,6 +222,11 @@ namespace Spring.Messaging.Nms.Support.Converter return dictionary; } + /// + /// Extracts the serializable object from the given object message. + /// + /// The message to convert. + /// The resulting serializable object. protected virtual object ExtractSerializableFromMessage( IObjectMessage message) { diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/DynamicDestinationResolver.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/DynamicDestinationResolver.cs index 19ae8811..393ff70b 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/DynamicDestinationResolver.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/DynamicDestinationResolver.cs @@ -33,7 +33,7 @@ namespace Spring.Messaging.Nms.Support.IDestinations /// Resolve the given destination name, either as located resource /// or as dynamic destination. /// - /// the current NMS ISession + /// the current NMS Session /// /// the name of the destination /// @@ -58,7 +58,7 @@ namespace Spring.Messaging.Nms.Support.IDestinations /// Resolve the given destination name to a Topic. - /// the current NMS ISession + /// the current NMS Session /// /// the name of the desired Topic. /// @@ -70,6 +70,14 @@ namespace Spring.Messaging.Nms.Support.IDestinations return session.GetTopic(topicName); } + /// Resolve the given destination name to a Queue. + /// the current NMS Session + /// + /// the name of the desired Queue. + /// + /// the NMS Queue name + /// + /// NMSException if resolution failed protected internal virtual IDestination ResolveQueue(ISession session, string queueName) { return session.GetQueue(queueName); diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/IDestinationResolver.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/IDestinationResolver.cs index f818c4af..921305ae 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/IDestinationResolver.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/Destinations/IDestinationResolver.cs @@ -43,7 +43,7 @@ namespace Spring.Messaging.Nms.Support.IDestinations /// Resolve the given destination name, either as located resource /// or as dynamic destination. /// - /// the current NMS ISession + /// the current NMS Session /// /// the name of the destination /// 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 dd0a17c3..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,14 +25,12 @@ using Apache.NMS; namespace Spring.Messaging.Nms.Support { - /// Base class for NmsTemplate and other - /// NMS-accessing gateway helpers - /// It defines common properties like the - /// IConnectionFactory}. The subclass - /// NmsIDestinationAccessor adds - /// further, destination-related properties. - /// + /// 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 NmsTemplate. + /// /// /// Juergen Hoeller /// Mark Pollack (.NET) @@ -47,7 +45,7 @@ namespace Spring.Messaging.Nms.Support #region Fields private IConnectionFactory connectionFactory; - private bool sessionTransacted = false; + private AcknowledgementMode sessionAcknowledgeMode = AcknowledgementMode.AutoAcknowledge; #endregion @@ -56,7 +54,7 @@ namespace Spring.Messaging.Nms.Support /// - /// Gets or sets the connection factory to use for obtaining NMS IConnections. + /// Gets or sets the connection factory to use for obtaining NMS Connections. /// /// The connection factory. virtual public IConnectionFactory ConnectionFactory @@ -78,8 +76,7 @@ namespace Spring.Messaging.Nms.Support /// /// /// Set the NMS acknowledgement mode that is used when creating a NMS - /// ISession to send a message. The default is ISession.AUTO_ACKNOWLEDGE. - ///

Vendor-specific extensions to the acknowledgment mode can be set here as well.

+ /// Session to send a message. The default is AUTO_ACKNOWLEDGE. ///
/// The session acknowledge mode. virtual public AcknowledgementMode SessionAcknowledgeMode @@ -112,7 +109,10 @@ namespace Spring.Messaging.Nms.Support /// public bool SessionTransacted { - get { return SessionAcknowledgeMode == AcknowledgementMode.Transactional; } + get + { + return SessionAcknowledgeMode == AcknowledgementMode.Transactional; + } set { if (value) @@ -125,6 +125,9 @@ namespace Spring.Messaging.Nms.Support #endregion + /// + /// Verify that ConnectionFactory property has been set. + /// public virtual void AfterPropertiesSet() { if (ConnectionFactory == null) @@ -142,6 +145,11 @@ namespace Spring.Messaging.Nms.Support return ConnectionFactory.CreateConnection(); } + /// + /// Creates the session for the given Connection + /// + /// The connection to create a session for. + /// The new session protected virtual ISession CreateSession(IConnection con) { return con.CreateSession(SessionAcknowledgeMode); @@ -150,9 +158,9 @@ namespace Spring.Messaging.Nms.Support /// /// Returns whether the ISession is in client acknowledgement mode. /// - /// The session. - /// true ifin client ack mode, false otherwise - protected virtual bool ClientAcknowledge(ISession session) + /// The session to check. + /// true if in client ack mode, false otherwise + protected virtual bool IsClientAcknowledge(ISession session) { return (session.AcknowledgementMode == AcknowledgementMode.ClientAcknowledge); } diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsUtils.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsUtils.cs index 91232f14..47f3134b 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsUtils.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsUtils.cs @@ -25,6 +25,10 @@ using Apache.NMS; namespace Spring.Messaging.Nms.Support { + /// + /// Generic utility methods for working with NMS. Mainly for internal use + /// within the framework, but also useful for custom NMS access code. + /// public abstract class NmsUtils { #region Logging @@ -33,20 +37,20 @@ namespace Spring.Messaging.Nms.Support #endregion - /// Close the given NMS IConnection and ignore any thrown exception. + /// Close the given NMS Connection and ignore any thrown exception. /// This is useful for typical finally blocks in manual NMS code. /// - /// the NMS IConnection to close (may be null) + /// the NMS Connection to close (may be null) /// public static void CloseConnection(IConnection con) { CloseConnection(con, false); } - /// Close the given NMS IConnection and ignore any thrown exception. + /// Close the given NMS Connection and ignore any thrown exception. /// This is useful for typical finally blocks in manual NMS code. /// - /// the NMS IConnection to close (may be null) + /// the NMS Connection to close (may be null) /// /// whether to call stop() before closing /// @@ -74,20 +78,20 @@ namespace Spring.Messaging.Nms.Support } catch (NMSException ex) { - logger.Debug("Could not close NMS IConnection", ex); + logger.Debug("Could not close NMS Connection", ex); } catch (Exception ex) { // We don't trust the NMS provider: It might throw another exception. - logger.Debug("Unexpected exception on closing NMS IConnection", ex); + logger.Debug("Unexpected exception on closing NMS Connection", ex); } } } - /// Close the given NMS ISession and ignore any thrown exception. + /// Close the given NMS Session and ignore any thrown exception. /// This is useful for typical finally blocks in manual NMS code. /// - /// the NMS ISession to close (may be null) + /// the NMS Session to close (may be null) /// public static void CloseSession(ISession session) { @@ -109,10 +113,10 @@ namespace Spring.Messaging.Nms.Support } } - /// Close the given NMS IMessageProducer and ignore any thrown exception. + /// Close the given NMS MessageProducer and ignore any thrown exception. /// This is useful for typical finally blocks in manual NMS code. /// - /// the NMS IMessageProducer to close (may be null) + /// the NMS MessageProducer to close (may be null) /// public static void CloseMessageProducer(IMessageProducer producer) { @@ -124,20 +128,20 @@ namespace Spring.Messaging.Nms.Support } catch (NMSException ex) { - logger.Debug("Could not close NMS IMessageProducer", ex); + logger.Debug("Could not close NMS MessageProducer", ex); } catch (Exception ex) { // We don't trust the NMS provider: It might throw RuntimeException or Error. - logger.Debug("Unexpected exception on closing NMS IMessageProducer", ex); + logger.Debug("Unexpected exception on closing NMS MessageProducer", ex); } } } - /// Close the given NMS IMessageConsumer and ignore any thrown exception. + /// Close the given NMS MessageConsumer and ignore any thrown exception. /// This is useful for typical finally blocks in manual NMS code. /// - /// the NMS IMessageConsumer to close (may be null) + /// the NMS MessageConsumer to close (may be null) /// public static void CloseMessageConsumer(IMessageConsumer consumer) { @@ -149,12 +153,12 @@ namespace Spring.Messaging.Nms.Support } catch (NMSException ex) { - logger.Debug("Could not close NMS IMessageConsumer", ex); + logger.Debug("Could not close NMS MessageConsumer", ex); } catch (Exception ex) { // We don't trust the NMS provider: It might throw RuntimeException or Error. - logger.Debug("Unexpected exception on closing NMS IMessageConsumer", ex); + logger.Debug("Unexpected exception on closing NMS MessageConsumer", ex); } } } @@ -186,11 +190,11 @@ namespace Spring.Messaging.Nms.Support // } - /// Commit the ISession if not within a distributed transaction. - /// Needs investigation - no distributed tx in EMS - /// the NMS ISession to commit + /// Commit the Session if not within a distributed transaction. + /// Needs investigation - no distributed tx in .NET messaging providers + /// the NMS Session to commit /// - /// NMSException if committing failed + /// NMSException if committing failed public static void CommitIfNecessary(ISession session) { AssertUtils.ArgumentNotNull(session, "ISession must not be null"); @@ -212,9 +216,9 @@ namespace Spring.Messaging.Nms.Support // } } - /// Rollback the ISession if not within a distributed transaction. + /// Rollback the Session if not within a distributed transaction. /// Needs investigation - no distributed tx in EMS - /// the NMS ISession to rollback + /// the NMS Session to rollback /// /// NMSException if committing failed public static void RollbackIfNecessary(ISession session) 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 d181f9b1..517aa631 100644 --- a/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj +++ b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj @@ -19,6 +19,7 @@ prompt 4 ..\..\..\build\VS.NET.2005\Spring.Messaging.Nms\Debug\Spring.Messaging.Nms.xml + true pdbonly @@ -67,7 +68,6 @@ -