NMS development

This commit is contained in:
markpollack
2008-07-25 04:45:15 +00:00
parent c467f49035
commit b6f610230f
36 changed files with 1373 additions and 426 deletions

View File

@@ -21,7 +21,7 @@
using System;
using Apache.NMS;
namespace Spring.Messaging.Nms.Connection
namespace Spring.Messaging.Nms.Connections
{
/// <summary>
/// MessageProducer decorator that adapts specific settings
@@ -31,7 +31,7 @@ namespace Spring.Messaging.Nms.Connection
/// <author>Mark Pollack (.NET)</author>
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;
/// <summary>
/// Initializes a new instance of the <see cref="CachedMessageProducer"/> class.
/// </summary>
/// <param name="target">The target.</param>
public CachedMessageProducer(IMessageProducer target)
{
this.target = target;
}
/// <summary>
/// Gets the target MessageProducer, the procder we are 'wrapping'
/// </summary>
/// <value>The target MessageProducer.</value>
public IMessageProducer Target
{
get { return target; }
}
/// <summary>
/// Sends the specified message.
/// </summary>
/// <param name="message">The message.</param>
public void Send(IMessage message)
{
target.Send(message);
}
/// <summary>
/// Sends a message to the specified message.
/// </summary>
/// <param name="message">The message to send.</param>
/// <param name="persistent">if set to <c>true</c> use persistent QOS.</param>
/// <param name="priority">The message priority.</param>
/// <param name="timeToLive">The time to live.</param>
public void Send(IMessage message, bool persistent, byte priority, TimeSpan timeToLive)
{
target.Send(message, persistent, priority, timeToLive);
}
/// <summary>
/// Sends a message to the specified destination.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="message">The message.</param>
public void Send(IDestination destination, IMessage message)
{
target.Send(destination, message);
}
/// <summary>
/// Sends a message the specified destination.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="message">The message to send.</param>
/// <param name="persistent">if set to <c>true</c> use persistent QOS.</param>
/// <param name="priority">The priority.</param>
/// <param name="timeToLive">The time to live.</param>
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.
/// <summary>
/// Creates the message.
/// </summary>
/// <returns>A new message</returns>
public IMessage CreateMessage()
{
return target.CreateMessage();
}
/// <summary>
/// Creates the text message.
/// </summary>
/// <returns>A new text message.</returns>
public ITextMessage CreateTextMessage()
{
return target.CreateTextMessage();
}
/// <summary>
/// Creates the text message.
/// </summary>
/// <param name="text">The text.</param>
/// <returns>A texst message with the given text.</returns>
public ITextMessage CreateTextMessage(string text)
{
return target.CreateTextMessage(text);
}
/// <summary>
/// Creates the map message.
/// </summary>
/// <returns>a new map message.</returns>
public IMapMessage CreateMapMessage()
{
return target.CreateMapMessage();
}
/// <summary>
/// Creates the object message.
/// </summary>
/// <param name="body">The body.</param>
/// <returns>A new object message with the given body.</returns>
public IObjectMessage CreateObjectMessage(object body)
{
return target.CreateObjectMessage(body);
}
/// <summary>
/// Creates the bytes message.
/// </summary>
/// <returns>A new bytes message.</returns>
public IBytesMessage CreateBytesMessage()
{
return target.CreateBytesMessage();
}
/// <summary>
/// Creates the bytes message.
/// </summary>
/// <param name="body">The body.</param>
/// <returns>A new bytes message with the given body.</returns>
public IBytesMessage CreateBytesMessage(byte[] body)
{
return target.CreateBytesMessage(body);
}
#endregion
/// <summary>
/// Gets or sets a value indicating whether this <see cref="CachedMessageProducer"/> uses a persistent QOS
/// </summary>
/// <value><c>true</c> if persistent; otherwise, <c>false</c>.</value>
public bool Persistent
{
get { return persistent; }
set { persistent = value; }
}
/// <summary>
/// Gets or sets the time to live value for messages sent with this producer.
/// </summary>
/// <value>The time to live.</value>
public TimeSpan TimeToLive
{
get { return timeToLive; }
set { timeToLive = value; }
}
/// <summary>
/// Gets or sets the priority of messages sent with this producer.
/// </summary>
/// <value>The priority.</value>
public byte Priority
{
get { return priority; }
set { priority = value;}
}
/// <summary>
/// Gets or sets a value indicating whether disable setting of the message ID property.
/// </summary>
/// <value><c>true</c> if disable message ID setting; otherwise, <c>false</c>.</value>
public bool DisableMessageID
{
get
@@ -153,6 +232,12 @@ namespace Spring.Messaging.Nms.Connection
}
}
/// <summary>
/// Gets or sets a value indicating whether disable setting the message timestamp property.
/// </summary>
/// <value>
/// <c>true</c> if disable message timestamp; otherwise, <c>false</c>.
/// </value>
public bool DisableMessageTimestamp
{
get
@@ -169,6 +254,9 @@ namespace Spring.Messaging.Nms.Connection
}
}
/// <summary>
/// Reset properties.
/// </summary>
public void Dispose()
{
// It's a cached MessageProducer... reset properties only.

View File

@@ -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
{
/// <summary>
/// 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
/// <summary>
/// Creates the consumer.
/// </summary>
/// <param name="destination">The destination.</param>
/// <returns></returns>
public IMessageConsumer CreateConsumer(IDestination destination)
{
return target.CreateConsumer(destination);
}
/// <summary>
/// Creates the consumer.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="selector">The selector.</param>
/// <returns></returns>
public IMessageConsumer CreateConsumer(IDestination destination, string selector)
{
return target.CreateConsumer(destination, selector);
}
/// <summary>
/// Creates the consumer.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="selector">The selector.</param>
/// <param name="noLocal">if set to <c>true</c> [no local].</param>
/// <returns></returns>
public IMessageConsumer CreateConsumer(IDestination destination, string selector, bool noLocal)
{
return target.CreateConsumer(destination, selector, noLocal);
}
/// <summary>
/// Creates the durable consumer.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="name">The name.</param>
/// <param name="selector">The selector.</param>
/// <param name="noLocal">if set to <c>true</c> [no local].</param>
/// <returns></returns>
public IMessageConsumer CreateDurableConsumer(ITopic destination, string name, string selector, bool noLocal)
{
return target.CreateDurableConsumer(destination, name, selector, noLocal);
}
/// <summary>
/// Gets the queue.
/// </summary>
/// <param name="name">The name.</param>
/// <returns></returns>
public IQueue GetQueue(string name)
{
return target.GetQueue(name);
}
/// <summary>
/// Gets the topic.
/// </summary>
/// <param name="name">The name.</param>
/// <returns></returns>
public ITopic GetTopic(string name)
{
return target.GetTopic(name);
}
/// <summary>
/// Creates the temporary queue.
/// </summary>
/// <returns></returns>
public ITemporaryQueue CreateTemporaryQueue()
{
return target.CreateTemporaryQueue();
}
/// <summary>
/// Creates the temporary topic.
/// </summary>
/// <returns></returns>
public ITemporaryTopic CreateTemporaryTopic()
{
return target.CreateTemporaryTopic();
}
/// <summary>
/// Creates the message.
/// </summary>
/// <returns></returns>
public IMessage CreateMessage()
{
return target.CreateMessage();
}
/// <summary>
/// Creates the text message.
/// </summary>
/// <returns></returns>
public ITextMessage CreateTextMessage()
{
return target.CreateTextMessage();
}
/// <summary>
/// Creates the text message.
/// </summary>
/// <param name="text">The text.</param>
/// <returns></returns>
public ITextMessage CreateTextMessage(string text)
{
return target.CreateTextMessage(text);
}
/// <summary>
/// Creates the map message.
/// </summary>
/// <returns></returns>
public IMapMessage CreateMapMessage()
{
return target.CreateMapMessage();
}
/// <summary>
/// Creates the object message.
/// </summary>
/// <param name="body">The body.</param>
/// <returns></returns>
public IObjectMessage CreateObjectMessage(object body)
{
return target.CreateObjectMessage(body);
}
/// <summary>
/// Creates the bytes message.
/// </summary>
/// <returns></returns>
public IBytesMessage CreateBytesMessage()
{
return target.CreateBytesMessage();
}
/// <summary>
/// Creates the bytes message.
/// </summary>
/// <param name="body">The body.</param>
/// <returns></returns>
public IBytesMessage CreateBytesMessage(byte[] body)
{
return target.CreateBytesMessage(body);
}
/// <summary>
/// Commits this instance.
/// </summary>
public void Commit()
{
target.Commit();
}
/// <summary>
/// Rollbacks this instance.
/// </summary>
public void Rollback()
{
target.Rollback();
}
/// <summary>
/// Gets a value indicating whether this <see cref="CachedSession"/> is transacted.
/// </summary>
/// <value><c>true</c> if transacted; otherwise, <c>false</c>.</value>
public bool Transacted
{
get { return target.Transacted; }
}
/// <summary>
/// Gets the acknowledgement mode.
/// </summary>
/// <value>The acknowledgement mode.</value>
public AcknowledgementMode AcknowledgementMode
{
get { return target.AcknowledgementMode; }
}
/// <summary>
/// Call dispose on the target.
/// </summary>
public void Dispose()
{
target.Dispose();

View File

@@ -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
{
/// <summary>
/// <see cref="SingleConnectionFactory"/> 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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="targetSession">The original Session to wrap.</param>
/// <param name="sessionList">The List of cached Sessions that the given Session belongs to.</param>
/// <returns>The wrapped Session</returns>
protected virtual ISession GetCachedSessionWrapper(ISession targetSession, LinkedList sessionList)
{
return new CachedSession(targetSession, sessionList, SessionCacheSize, CacheProducers);

View File

@@ -23,7 +23,7 @@ using System;
using System.Collections;
using Spring.Util;
namespace Spring.Messaging.Nms.Connection
namespace Spring.Messaging.Nms.Connections
{
/// <summary>
/// Implementation of Spring IExceptionListener interface that supports

View File

@@ -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
{
/// <summary> Helper class for obtaining transactional NMS resources
/// for a given IConnectionFactory.
///
/// for a given ConnectionFactory.
/// </summary>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
@@ -40,19 +40,76 @@ namespace Spring.Messaging.Nms.Connection
#endregion
/// <summary> Obtain a NMS ISession that is synchronized with the current transaction, if any.</summary>
/// <param name="cf">the IConnectionFactory to obtain a ISession for
/// <summary>
/// Releases the given connection, stopping it (if necessary) and eventually closing it.
/// </summary>
/// <remarks>Checks <see cref="ISmartConnectionFactory.ShouldStop"/>, if available.
/// This is essentially a more sophisticated version of
/// <see cref="NmsUtils.CloseConnection(IConnection, bool)"/>
/// </remarks>
/// <param name="connection">The connection to release. (if this is <code>null</code>, the call will be ignored)</param>
/// <param name="cf">The ConnectionFactory that the Connection was obtained from. (may be <code>null</code>)</param>
/// <param name="started">whether the Connection might have been started by the application.</param>
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);
}
}
/// <summary>
/// Determines whether the given JMS Session is transactional, that is,
/// bound to the current thread by Spring's transaction facilities.
/// </summary>
/// <param name="session">The session to check.</param>
/// <param name="cf">The ConnectionFactory that the Session originated from</param>
/// <returns>
/// <c>true</c> if is session transactional, bound to current thread; otherwise, <c>false</c>.
/// </returns>
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));
}
/// <summary> Obtain a NMS Session that is synchronized with the current transaction, if any.</summary>
/// <param name="cf">the ConnectionFactory to obtain a Session for
/// </param>
/// <param name="existingCon">the existing NMS IConnection to obtain a ISession for
/// <param name="existingCon">the existing NMS Connection to obtain a Session for
/// (may be <code>null</code>)
/// </param>
/// <param name="synchedLocalTransactionAllowed">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.
/// </param>
/// <returns> the transactional ISession, or <code>null</code> if none found
/// <returns> the transactional Session, or <code>null</code> if none found
/// </returns>
/// <throws> NMSException in case of NMS failure </throws>
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);
}
/// <summary> Obtain a NMS ISession that is synchronized with the current transaction, if any.</summary>
/// <summary>
/// Obtain a NMS Session that is synchronized with the current transaction, if any.
/// </summary>
/// <param name="resourceKey">the TransactionSynchronizationManager key to bind to
/// (usually the IConnectionFactory)
/// </param>
/// (usually the ConnectionFactory)</param>
/// <param name="resourceFactory">the ResourceFactory to use for extracting or creating
/// NMS resources
/// </param>
/// <returns> the transactional ISession, or <code>null</code> if none found
/// NMS resources</param>
/// <param name="startConnection">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 <code>false</code>.</param>
/// <returns>
/// the transactional Session, or <code>null</code> if none found
/// </returns>
/// <throws>NMSException in case of NMS failure </throws>
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
/// </summary>
public interface ResourceFactory
{
/// <summary> Fetch an appropriate ISession from the given NmsResourceHolder.</summary>
/// <summary> Fetch an appropriate Session from the given NmsResourceHolder.</summary>
/// <param name="holder">the NmsResourceHolder
/// </param>
/// <returns> an appropriate ISession fetched from the holder,
/// <returns> an appropriate Session fetched from the holder,
/// or <code>null</code> if none found
/// </returns>
ISession GetSession(NmsResourceHolder holder);
/// <summary> Fetch an appropriate IConnection from the given NmsResourceHolder.</summary>
/// <summary> Fetch an appropriate Connection from the given NmsResourceHolder.</summary>
/// <param name="holder">the NmsResourceHolder
/// </param>
/// <returns> an appropriate IConnection fetched from the holder,
/// <returns> an appropriate Connection fetched from the holder,
/// or <code>null</code> if none found
/// </returns>
IConnection GetConnection(NmsResourceHolder holder);
/// <summary> Create a new NMS IConnection for registration with a NmsResourceHolder.</summary>
/// <returns> the new NMS IConnection
/// <summary> Create a new NMS Connection for registration with a NmsResourceHolder.</summary>
/// <returns> the new NMS Connection
/// </returns>
/// <throws>NMSException if thrown by NMS API methods </throws>
IConnection CreateConnection();
/// <summary> Create a new NMS ISession for registration with a NmsResourceHolder.</summary>
/// <param name="con">the NMS IConnection to create a ISession for
/// <param name="con">the NMS Connection to create a ISession for
/// </param>
/// <returns> the new NMS ISession
/// <returns> the new NMS Session
/// </returns>
/// <throws>NMSException if thrown by NMS API methods </throws>
ISession CreateSession(IConnection con);

View File

@@ -20,11 +20,11 @@
using Apache.NMS;
namespace Spring.Messaging.Nms.Connection
namespace Spring.Messaging.Nms.Connections
{
/// <summary>
/// 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.
/// </summary>
/// <author>Mark Pollack</author>

View File

@@ -22,10 +22,10 @@
using Apache.NMS;
namespace Spring.Messaging.Nms.Connection
namespace Spring.Messaging.Nms.Connections
{
/// <summary>
/// Extension of the <code>IConnectionFactory</code> interface,
/// Extension of the <code>ConnectionFactory</code> interface,
/// indicating how to release Connections obtained from it.
/// </summary>
/// <author>Juergen Hoeller</author>

View File

@@ -26,11 +26,11 @@ using Spring.Transaction.Support;
using Spring.Util;
using Apache.NMS;
namespace Spring.Messaging.Nms.Connection
namespace Spring.Messaging.Nms.Connections
{
/// <summary> IConnection holder, wrapping a NMS IConnection and a NMS ISession.
/// <summary>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.
///
/// <p>Note: This is an SPI class, not intended to be used by applications.</p>
///
@@ -92,17 +92,23 @@ namespace Spring.Messaging.Nms.Connection
}
/// <summary> Create a new NmsResourceHolder for the given NMS resources.</summary>
/// <param name="connection">the NMS IConnection
/// <param name="connection">the NMS Connection
/// </param>
/// <param name="session">the NMS ISession
/// <param name="session">the NMS Session
/// </param>
public NmsResourceHolder(Apache.NMS.IConnection connection, ISession session)
public NmsResourceHolder(IConnection connection, ISession session)
{
AddConnection(connection);
AddSession(session, connection);
this.frozen = true;
}
/// <summary>
/// Initializes a new instance of the <see cref="NmsResourceHolder"/> class.
/// </summary>
/// <param name="connectionFactory">The connection factory.</param>
/// <param name="connection">The connection.</param>
/// <param name="session">The session.</param>
public NmsResourceHolder(IConnectionFactory connectionFactory, IConnection connection, ISession session)
{
this.connectionFactory = connectionFactory;
@@ -114,6 +120,12 @@ namespace Spring.Messaging.Nms.Connection
#region Properties
/// <summary>
/// Gets a value indicating whether this <see cref="NmsResourceHolder"/> 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.
/// </summary>
/// <value><c>true</c> if frozen; otherwise, <c>false</c>.</value>
virtual public bool Frozen
{
get
@@ -125,11 +137,14 @@ namespace Spring.Messaging.Nms.Connection
#endregion
#region Methods
public void AddConnection(Apache.NMS.IConnection connection)
/// <summary>
/// Adds the connection to the list of resources managed by this holder.
/// </summary>
/// <param name="connection">The connection.</param>
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
}
}
/// <summary>
/// Adds the session to the list of resources managed by this holder.
/// </summary>
/// <param name="session">The session.</param>
public void AddSession(ISession session)
{
AddSession(session, null);
}
public void AddSession(ISession session, Apache.NMS.IConnection connection)
/// <summary>
/// Adds the session and connection to the list of resources managed by this holder.
/// </summary>
/// <param name="session">The session.</param>
/// <param name="connection">The connection.</param>
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()
/// <summary>
/// Gets the connection managed by this resource holder
/// </summary>
/// <returns>A Connection, or null if no managed connection.</returns>
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)
/// <summary>
/// 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.
/// </summary>
/// <param name="connectionType">Type of the connection.</param>
/// <returns>The connection, or null if not found.</returns>
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);
}
/// <summary>
/// Gets the first session manged by this holder or null if not available.
/// </summary>
/// <returns>The session or null if not available.</returns>
public virtual ISession GetSession()
{
return (!(this.sessions.Count == 0) ? (ISession)this.sessions[0] : null);
}
/// <summary>
/// Gets the session managed by this holder by type.
/// </summary>
/// <param name="sessionType">Type of the session.</param>
/// <returns>The session or null if not available.</returns>
public virtual ISession GetSession(Type sessionType)
{
return GetSession(sessionType, null);
}
public virtual ISession GetSession(System.Type sessionType, Apache.NMS.IConnection connection)
/// <summary>
/// Gets the session of a given type associated with the given connection
/// </summary>
/// <param name="sessionType">Type of the session.</param>
/// <param name="connection">The connection.</param>
/// <returns>The sessin or null if not available.</returns>
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);
}
/// <summary>
@@ -230,6 +275,13 @@ namespace Spring.Messaging.Nms.Connection
}
}
/// <summary>
/// Determines whether the holder contains the specified session.
/// </summary>
/// <param name="session">The session.</param>
/// <returns>
/// <c>true</c> if the holder contains the specified session; otherwise, <c>false</c>.
/// </returns>
public bool ContainsSession(ISession session)
{
return this.sessions.Contains(session);

View File

@@ -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
{
/// <summary>
/// A <see cref="AbstractPlatformTransactionManager"/> implementation
/// for a single NMS <code>Apache.NMS.IConnectionFactory</code>. Binds a
/// for a single NMS <code>ConnectionFactory</code>. Binds a
/// Connection/Session pair from the specified ConnecctionFactory to the thread,
/// potentially allowing for one thread-bound Session per ConnectionFactory.
/// </summary>
@@ -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
/// <summary>
/// Get the NmsTransactionObject.
/// </summary>
/// <returns>he NmsTransactionObject.</returns>
protected override object DoGetTransaction()
{
NmsTransactionObject txObject = new NmsTransactionObject();
@@ -135,6 +157,21 @@ namespace Spring.Messaging.Nms.Connection
return txObject;
}
/// <summary>
/// Begin a new transaction with the given transaction definition.
/// </summary>
/// <param name="transaction">Transaction object returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.</param>
/// <param name="definition"><see cref="Spring.Transaction.ITransactionDefinition"/> instance, describing
/// propagation behavior, isolation level, timeout etc.</param>
/// <remarks>
/// Does not have to care about applying the propagation behavior,
/// as this has already been handled by this abstract manager.
/// </remarks>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of creation or system errors.
/// </exception>
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
}
/// <summary>
/// Suspend the resources of the current transaction.
/// </summary>
/// <param name="transaction">Transaction object returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.</param>
/// <returns>
/// An object that holds suspended resources (will be kept unexamined for passing it into
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoResume"/>.)
/// </returns>
/// <remarks>
/// Transaction synchronization will already have been suspended.
/// </remarks>
/// <exception cref="Spring.Transaction.TransactionException">
/// in case of system errors.
/// </exception>
protected override object DoSuspend(object transaction)
{
NmsTransactionObject txObject = (NmsTransactionObject) transaction;
@@ -193,12 +245,31 @@ namespace Spring.Messaging.Nms.Connection
return TransactionSynchronizationManager.UnbindResource(ConnectionFactory);
}
/// <summary>
/// Resume the resources of the current transaction.
/// </summary>
/// <param name="transaction">Transaction object returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.</param>
/// <param name="suspendedResources">The object that holds suspended resources as returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoSuspend"/>.</param>
/// <remarks>Transaction synchronization will be resumed afterwards.
/// </remarks>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
protected override void DoResume(object transaction, object suspendedResources)
{
NmsResourceHolder conHolder = (NmsResourceHolder) suspendedResources;
TransactionSynchronizationManager.BindResource(ConnectionFactory, conHolder);
}
/// <summary>
/// Perform an actual commit on the given transaction.
/// </summary>
/// <param name="status">The status representation of the transaction.</param>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
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);
}
}
/// <summary>
/// Perform an actual rollback on the given transaction.
/// </summary>
/// <param name="status">The status representation of the transaction.</param>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
protected override void DoRollback(DefaultTransactionStatus status)
{
NmsTransactionObject txObject = (NmsTransactionObject)status.Transaction;
@@ -242,12 +316,33 @@ namespace Spring.Messaging.Nms.Connection
}
/// <summary>
/// Set the given transaction rollback-only. Only called on rollback
/// if the current transaction takes part in an existing one.
/// </summary>
/// <param name="status">The status representation of the transaction.</param>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
protected override void DoSetRollbackOnly(DefaultTransactionStatus status)
{
NmsTransactionObject txObject = (NmsTransactionObject)status.Transaction;
txObject.ResourceHolder.RollbackOnly = true;
}
/// <summary>
/// Cleanup resources after transaction completion.
/// </summary>
/// <param name="transaction">Transaction object returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.</param>
/// <remarks>
/// <para>
/// Called after <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoCommit"/>
/// and
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoRollback"/>
/// execution on any outcome.
/// </para>
/// </remarks>
protected override void DoCleanupAfterCompletion(object transaction)
{
NmsTransactionObject txObject = (NmsTransactionObject)transaction;
@@ -256,6 +351,18 @@ namespace Spring.Messaging.Nms.Connection
txObject.ResourceHolder.Clear();
}
/// <summary>
/// Check if the given transaction object indicates an existing transaction
/// (that is, a transaction which has already started).
/// </summary>
/// <param name="transaction">Transaction object returned by
/// <see cref="Spring.Transaction.Support.AbstractPlatformTransactionManager.DoGetTransaction"/>.</param>
/// <returns>
/// True if there is an existing transaction.
/// </returns>
/// <exception cref="Spring.Transaction.TransactionException">
/// In the case of system errors.
/// </exception>
protected override bool IsExistingTransaction(object transaction)
{
NmsTransactionObject txObject = transaction as NmsTransactionObject;

View File

@@ -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
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// You can either pass in a specific Connection directly or let this
/// factory lazily create a Connection via a given target ConnectionFactory.
/// <para>Useful in order to keep using the same Connection for multiple
/// <see cref="NmsTemplate"/> calls, without having a pooling ConnectionFactory
/// underneath. This may span any number of transactions, even concurrently executing transactions.
/// </para>
/// <para>
/// 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.
/// </para>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack</author>
/// <author>Mark Pollack (.NET)</author>
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
}
/// <summary>
/// Gets or sets the exception listener implementation that should be registered
/// with with the single Connection created by this factory, if any.
/// </summary>
/// <value>The exception listener.</value>
public IExceptionListener ExceptionListener
{
get { return exceptionListener; }
@@ -162,6 +192,10 @@ namespace Spring.Messaging.Nms.Connection
#region IConnectionFactory Members
/// <summary>
/// Creates the connection.
/// </summary>
/// <returns>A single shared connection</returns>
public IConnection CreateConnection()
{
lock (connectionMonitor)
@@ -174,6 +208,12 @@ namespace Spring.Messaging.Nms.Connection
}
}
/// <summary>
/// Creates the connection.
/// </summary>
/// <param name="userName">Name of the user.</param>
/// <param name="password">The password.</param>
/// <returns></returns>
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
/// <summary>
/// Initialize the underlying shared Connection. Closes and reinitializes the Connection if an underlying
/// Connection is present already.
/// </summary>
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();
}
/// <summary>
/// Prepares the connection before it is exposed.
/// The default implementation applies ExceptionListener and client id.
/// Can be overridden in subclasses.
/// </summary>
/// <param name="con">The Connection to prepare.</param>
/// <exception cref="NMSException">if thrown by any NMS API methods.</exception>
protected virtual void PrepareConnection(IConnection con)
{
if (ClientId != null)
@@ -249,11 +300,19 @@ namespace Spring.Messaging.Nms.Connection
return null;
}
/// <summary>
/// reate a JMS Connection via this template's ConnectionFactory.
/// </summary>
/// <returns></returns>
protected virtual IConnection DoCreateConnection()
{
return TargetConnectionFactory.CreateConnection();
}
/// <summary>
/// Closes the given connection.
/// </summary>
/// <param name="con">The connection.</param>
protected virtual void CloseConnection(IConnection con)
{
try
@@ -273,6 +332,9 @@ namespace Spring.Messaging.Nms.Connection
#region IInitializingObject Members
/// <summary>
/// Ensure that the connection or TargetConnectionFactory are specified.
/// </summary>
public void AfterPropertiesSet()
{
if (connection == null && TargetConnectionFactory == null)
@@ -283,11 +345,20 @@ namespace Spring.Messaging.Nms.Connection
#endregion
/// <summary>
/// Close the underlying shared connection. The provider of this ConnectionFactory needs to care for proper shutdown.
/// As this object implements <see cref="IDisposable"/> an application context will automatically
/// invoke this on distruction o
/// </summary>
public void Dispose()
{
ResetConnection();
}
/// <summary>
/// Resets the underlying shared Connection, to be reinitialized on next access.
/// </summary>
public virtual void ResetConnection()
{
lock (connectionMonitor)
@@ -301,11 +372,19 @@ namespace Spring.Messaging.Nms.Connection
}
}
protected virtual IConnection GetSharedConnection(SingleConnectionFactory singleConnectionFactory, IConnection target)
/// <summary>
/// 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.
/// </summary>
/// <param name="target">The original connection to wrap.</param>
/// <returns>the wrapped connection</returns>
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();

View File

@@ -21,7 +21,7 @@
using System;
using Apache.NMS;
namespace Spring.Messaging.Nms.Connection
namespace Spring.Messaging.Nms.Connections
{
/// <summary> Exception thrown when a synchronized local transaction failed to complete
/// (after the main transaction has already completed).

View File

@@ -28,6 +28,10 @@ namespace Spring.Messaging.Nms
/// <author>Mark Pollack</author>
public interface IExceptionListener
{
/// <summary>
/// Called when there is an exception in message processing.
/// </summary>
/// <param name="exception">The exception.</param>
void OnException(Exception exception);
}
}

View File

@@ -22,19 +22,19 @@ using Apache.NMS;
namespace Spring.Messaging.Nms
{
/// <summary> Creates a NMS message given a ISession</summary>
/// <summary> Creates a NMS message given a Session</summary>
/// <remarks>
/// <p>The <code>ISession</code> typically is provided by an instance
/// <p>The Session typically is provided by an instance
/// of the NmsTemplate class.</p>
/// </remarks>
/// <author>Mark Pollack</author>
public interface IMessageCreator
{
/// <summary> Create a IMessage to be sent.</summary>
/// <param name="session">the NMS ISession to be used to create the
/// <summary> Create a Message to be sent.</summary>
/// <param name="session">the NMS Session to be used to create the
/// <code>IMessage</code> (never <code>null</code>)
/// </param>
/// <returns> the <code>IMessage</code> to be sent
/// <returns> the <code>Message</code> to be sent
/// </returns>
/// <throws>NMSException if thrown by NMS API methods </throws>
IMessage CreateMessage(ISession session);

View File

@@ -22,8 +22,15 @@ using Apache.NMS;
namespace Spring.Messaging.Nms
{
/// <summary>
/// Interfaced based approach to listen to messaging events.
/// </summary>
public interface IMessageListener
{
/// <summary>
/// Called when a message is delivered.
/// </summary>
/// <param name="message">The message.</param>
void OnMessage(IMessage message);
}
}

View File

@@ -37,7 +37,7 @@ namespace Spring.Messaging.Nms
/// </summary>
/// <param name="message">the NMS message from the IMessageConverter
/// </param>
/// <returns> the modified version of the IMessage
/// <returns> the modified version of the Message
/// </returns>
/// <throws>NMSException if thrown by NMS API methods </throws>
IMessage PostProcessMessage(IMessage message);

View File

@@ -39,11 +39,13 @@ namespace Spring.Messaging.Nms
public interface INmsOperations
{
/// <summary> Execute the action specified by the given action object within
/// a NMS ISession.
/// <p>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.</p>
/// a NMS Session.
/// </summary>
/// <remarks>
/// <para>Note that the value of PubSubDomain affects the behavior of this method.
/// If PubSubDomain equals true, then a Session is passed to the callback.
/// If false, then a ISession is passed to the callback.</para>b
/// </remarks>
/// <param name="action">callback object that exposes the session
/// </param>
/// <returns> the result object from working with the session
@@ -52,7 +54,7 @@ namespace Spring.Messaging.Nms
object Execute(ISessionCallback action);
/// <summary> 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.
/// </summary>
/// <param name="action">callback object that exposes the session/producer pair
@@ -76,7 +78,7 @@ namespace Spring.Messaging.Nms
void Send(IMessageCreator messageCreator);
/// <summary> 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.
/// </summary>
/// <param name="destination">the destination to send this message to
/// </param>
@@ -86,7 +88,7 @@ namespace Spring.Messaging.Nms
void Send(IDestination destination, IMessageCreator messageCreator);
/// <summary> 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.
/// </summary>
/// <param name="destinationName">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);
/// <summary> 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.
/// </summary>
/// <param name="destination">the destination to send this message to
/// </param>
@@ -119,7 +121,7 @@ namespace Spring.Messaging.Nms
void SendWithDelegate(IDestination destination, IMessageCreatorDelegate messageCreatorDelegate);
/// <summary> 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.
/// </summary>
/// <param name="destinationName">the name of the destination to send this message to
/// (to be resolved to an actual destination by a DestinationResolver)

View File

@@ -28,19 +28,19 @@ namespace Spring.Messaging.Nms
/// method, often implemented as an anonymous inner class.</p>
///
/// <p>The typical implementation will perform multiple operations on the
/// supplied NMS ISession and IMessageProducer. </p>
/// supplied NMS Session and MessageProducer. </p>
/// </remarks>
/// <author>Mark Pollack</author>
public interface IProducerCallback
{
/// <summary> Perform operations on the given ISession and IMessageProducer.
/// <summary> Perform operations on the given Session and MessageProducer.
/// The message producer is not associated with any destination.
/// </summary>
/// <param name="session">the NMS <code>ISession</code> object to use
/// <param name="session">the NMS <code>Session</code> object to use
/// </param>
/// <param name="producer">the NMS <code>IMessageProducer</code> object to use
/// <param name="producer">the NMS <code>MessageProducer</code> object to use
/// </param>
/// <returns> a result object from working with the <code>ISession</code>, if any (can be <code>null</code>)
/// <returns> a result object from working with the <code>Session</code>, if any (can be <code>null</code>)
/// </returns>
object DoInNms(ISession session, IMessageProducer producer);

View File

@@ -23,7 +23,7 @@ using Apache.NMS;
namespace Spring.Messaging.Nms
{
/// <summary> Callback for executing any number of operations on a provided
/// ISession
/// Session
/// </summary>
/// <remarks>
/// <p>To be used with the NmsTemplate.Execute(ISessionCallback)}
@@ -35,11 +35,11 @@ namespace Spring.Messaging.Nms
public interface ISessionCallback
{
/// <summary> Execute any number of operations against the supplied NMS
/// ISession, possibly returning a result.
/// Session, possibly returning a result.
/// </summary>
/// <param name="session">the NMS <code>ISession</code>
/// <param name="session">the NMS <code>Session</code>
/// </param>
/// <returns> a result object from working with the <code>ISession</code>, if any (so can be <code>null</code>)
/// <returns> a result object from working with the <code>Session</code>, if any (so can be <code>null</code>)
/// </returns>
/// <throws>NMSException if there is any problem </throws>
object DoInNms(ISession session);

View File

@@ -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
{
/// <summary>
/// Abstract base class for message listener containers. Can either host
/// a standard NMS <see cref="IMessageListener"/> or a Spring-specific
/// a standard NMS MessageListener or a Spring-specific
/// <see cref="ISessionAwareMessageListener"/>
/// </summary>
public abstract class AbstractMessageListenerContainer : AbstractNmsListeningContainer
@@ -61,6 +62,12 @@ namespace Spring.Messaging.Nms.Listener
#region Properties
/// <summary>
/// Gets or sets the destination to receive messages from. Will be <code>null</code>
/// if the configured destination is not an actual Destination type;
/// c.f. <see cref="DestinationName"/> when the destination is a String.
/// </summary>
/// <value>The destination.</value>
public IDestination Destination
{
get
@@ -80,6 +87,12 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary>
/// Gets or sets the name of the destination to receive messages from.
/// Will be <code>null</code> if the configured destination is not a
/// string type; c.f. <see cref="Destination"/> when it is an actual Destination object.
/// </summary>
/// <value>The name of the destination.</value>
public string DestinationName
{
get
@@ -95,6 +108,10 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary>
/// Gets or sets the message selector.
/// </summary>
/// <value>The message selector expression (or <code>null</code> if none)..</value>
public string MessageSelector
{
get { return messageSelector; }
@@ -108,7 +125,7 @@ namespace Spring.Messaging.Nms.Listener
///
/// <remarks>
/// <para>
/// This can be either a standard NMS <see cref="IMessageListener"/> object or a
/// This can be either a standard NMS MessageListener object or a
/// Spring <see cref="ISessionAwareMessageListener"/> object.
/// </para>
/// </remarks>
@@ -132,6 +149,19 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary>
/// Gets or sets a value indicating whether the subscription is durable.
/// </summary>
/// <remarks>
/// Set whether to make the subscription durable. The durable subscription name
/// to be used can be specified through the "DurableSubscriptionName" property.
/// <para>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).
/// </para>
/// <para>Only makes sense when listening to a topic (pub-sub domain).</para>
/// </remarks>
/// <value><c>true</c> if the subscription is durable; otherwise, <c>false</c>.</value>
public bool SubscriptionDurable
{
get { return subscriptionDurable; }
@@ -139,6 +169,18 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary>
/// Gets or sets the name of the durable subscription to create.
/// </summary>
/// <remarks>
/// 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.
/// <para>Note: Only 1 concurrent consumer (which is the default of this
/// message listener container) is allowed for each durable subscription.
/// </para>
/// </remarks>
/// <value>The name of the durable subscription.</value>
public string DurableSubscriptionName
{
get
@@ -153,6 +195,11 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary>
/// Gets or sets the exception listener to notify in case of a NMSException thrown
/// by the registered message listener or the invocation infrastructure.
/// </summary>
/// <value>The exception listener.</value>
public IExceptionListener ExceptionListener
{
get { return exceptionListener; }
@@ -160,6 +207,24 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary>
/// Gets or sets a value indicating whether to expose listener session to a registered
/// <see cref="ISessionAwareMessageListener"/> as well as to <see cref="NmsTemplate"/> calls.
/// </summary>
/// <remarks>
/// 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.
/// <para>Note that Sessions managed by an external transaction manager will
/// always get exposed to <see cref="NmsTemplate"/>
/// calls. So in terms of NmsTemplate exposure, this setting only affects
/// locally transacted Sessions.
/// </para>
/// </remarks>
/// <value>
/// <c>true</c> if expose listener session; otherwise, <c>false</c>.
/// </value>
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
/// <summary>
/// Validate that the destination is not null and that if the subscription is durable, then we are not
/// using the Pub/Sub domain.
/// </summary>
protected override void ValidateConfiguration()
{
if (this.destination == null)
@@ -284,7 +346,7 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary>
/// Invokes the specified listener: either as standard NMS IMessageListener
/// Invokes the specified listener: either as standard NMS MessageListener
/// or (preferably) as Spring SessionAwareMessageListener.
/// </summary>
/// <param name="session">The session to operate on.</param>
@@ -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();
}
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="session">The session to check.</param>
/// <returns>
/// <c>true</c> if the is session locally transacted; otherwise, <c>false</c>.
/// </returns>
/// <see cref="NmsAccessor.SessionTransacted"/>
protected virtual bool IsSessionLocallyTransacted(ISession session)
{
return SessionTransacted;
}
/// <summary>
/// Perform a rollback, if appropriate.
@@ -403,7 +486,7 @@ namespace Spring.Messaging.Nms.Listener
/// <exception cref="NMSException">In case of a rollback error</exception>
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)
/// <summary>
/// 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 <see cref="ISessionAwareMessageListener"/> object will be accepted.
/// </summary>
/// <param name="messageListener">The message listener.</param>
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 + "]");
}
}
}

View File

@@ -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;
/// <summary>
/// The monitor object to lock on when performing operations on the connection.
/// </summary>
protected object sharedConnectionMonitor = new object();
private volatile bool active = false;
private bool running = false;
/// <summary>
/// The monitor object to lock on when performing operations that update the lifecycle of the container.
/// </summary>
protected object lifecycleMonitor = new object();
#endregion
/// <summary>
/// Gets or sets the client id for a shared Connection created and used by this container.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <value>The client id.</value>
public string ClientId
{
set { clientId = value; }
@@ -89,6 +104,18 @@ namespace Spring.Messaging.Nms.Listener
set { this.autoStartup = value; }
}
/// <summary>
/// Set the name of the object in the object factory that created this object.
/// </summary>
/// <value>The name of the object in the factory.</value>
/// <remarks>
/// <p>
/// Invoked after population of normal object properties but before an init
/// callback like <see cref="Spring.Objects.Factory.IInitializingObject"/>'s
/// <see cref="Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/>
/// method or a custom init-method.
/// </p>
/// </remarks>
public string ObjectName
{
set { objectName = value; }
@@ -134,6 +161,11 @@ namespace Spring.Messaging.Nms.Listener
return true; }
}
/// <summary>
/// Gets a value indicating whether this container is currently active,
/// that is, whether it has been set up but not shut down yet.
/// </summary>
/// <value><c>true</c> if active; otherwise, <c>false</c>.</value>
public virtual bool Active
{
get
@@ -146,11 +178,10 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary> Return whether a shared NMS IConnection should be maintained
/// <summary> Return whether a shared NMS Connection should be maintained
/// by this listener container base class.
/// </summary>
/// <seealso cref="AbstractMessageListenerContainer.SharedConnection">
/// </seealso>
/// </summary>
/// <seealso cref="SharedConnection"/>
protected abstract bool SharedConnectionEnabled { get; }
/// <summary>
@@ -180,7 +211,10 @@ namespace Spring.Messaging.Nms.Listener
}
}
}
/// <summary>
/// Call base class method, then <see cref="ValidateConfiguration"/> and then <see cref="Initialize"/>
/// </summary>
public override void AfterPropertiesSet()
{
base.AfterPropertiesSet();
@@ -197,6 +231,9 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary>
/// Calls <see cref="Shutdown"/> when the application context destroys the container instance.
/// </summary>
public void Dispose()
{
Shutdown();
@@ -237,6 +274,9 @@ namespace Spring.Messaging.Nms.Listener
}
}
/// <summary>
/// Stop the shared connection, call <see cref="DoShutdown"/>, and close this container.
/// </summary>
public virtual void Shutdown()
{
logger.Debug("Shutting down message listener container");
@@ -283,6 +323,9 @@ namespace Spring.Messaging.Nms.Listener
DoStart();
}
/// <summary>
/// Start the shared Connection, if any, and notify all invoker tasks.
/// </summary>
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
}
}
/// <summary>
/// Stops the shared connection.
/// </summary>
/// <exception cref="NMSException">if thrown by NMS API methods.</exception>
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.
/// </summary>
public class SharedConnectionNotInitializedException : ApplicationException
public class SharedConnectionNotInitializedException : NMSException
{
/// <summary>
/// Initializes a new instance of the <see cref="SharedConnectionNotInitializedException"/> class.

View File

@@ -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
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// <para>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
/// <see cref="IMessageConverter"/>. By default, a <see cref="SimpleMessageConverter"/>
/// will be used. (If you do not want such automatic message conversion taking
/// place, then be sure to set the <see cref="MessageConverter"/> property
/// to <code>null</code>.)
/// </para>
/// <para>If a target listener method returns a non-null object (typically of a
/// message content type such as <code>String</code> or byte array), it will get
/// wrapped in a NMS <code>Message</code> and sent to the response destination
/// (either the NMS "reply-to" destination or the <see cref="defaultResponseDestination"/>
/// specified.
/// </para>
/// <para>
/// The sending of response messages is only available when
/// using the <see cref="ISessionAwareMessageListener"/> entry point (typically through a
/// Spring message listener container). Usage as standard NMS MessageListener
/// does <i>not</i> support the generation of response messages.
/// </para>
/// <para>Consult the reference documentation for examples of method signatures compliant with this
/// adapter class.
/// </para>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
public class MessageListenerAdapter : IMessageListener
{
#region Logging
@@ -18,9 +53,14 @@ namespace Spring.Messaging.Nms.Listener.Adapter
#endregion
private object delegateObject;
/// <summary>
/// The default handler method name.
/// </summary>
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;
/// <summary>
/// Initializes a new instance of the <see cref="MessageListenerAdapter"/> class with default settings.
/// </summary>
public MessageListenerAdapter()
{
InitDefaultStrategies();
delegateObject = this;
processingExpression = Spring.Expressions.Expression.Parse(defaultListenerMethod + "(#convertedObject)");
handlerObject = this;
processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
}
public MessageListenerAdapter(object delegateObject)
/// <summary>
/// Initializes a new instance of the <see cref="MessageListenerAdapter"/> class for the given handler object
/// </summary>
/// <param name="handlerObject">The delegate object.</param>
public MessageListenerAdapter(object handlerObject)
{
InitDefaultStrategies();
this.delegateObject = delegateObject;
this.handlerObject = handlerObject;
}
// TODO name change?
public object DelegateObject
/// <summary>
/// Gets or sets the handler object to delegate message listening to.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <value>The handler object.</value>
public object HandlerObject
{
get { return delegateObject; }
set { delegateObject = value; }
get { return handlerObject; }
set { handlerObject = value; }
}
public string DefaultListenerMethod
/// <summary>
/// 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 <see cref="ORIGINAL_DEFAULT_HANDLER_METHOD"/> ("HandleMessage"}.
/// </summary>
/// <value>The default handler method.</value>
public string DefaultHandlerMethod
{
get { return defaultListenerMethod; }
get { return defaultHandlerMethod; }
set
{
defaultListenerMethod = value;
defaultHandlerMethod = value;
}
}
/// <summary>
/// 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.
/// <para>
/// Alternatively, specify a "DefaultResponseQueueName" or "DefaultResponseTopicName",
/// to be dynamically resolved via the DestinationResolver.
/// </para>
/// </summary>
/// <value>The default response destination.</value>
public object DefaultResponseDestination
{
set { defaultResponseDestination = value; }
}
/// <summary>
/// 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.
/// <para>Alternatively, specify a JMS Destination object as "defaultResponseDestination".</para>
/// </summary>
/// <value>The name of the default response destination queue.</value>
public string DefaultResponseDestinationQueueName
{
set { defaultResponseDestination = new DestinationNameHolder(value, false); }
}
/// <summary>
/// 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.
/// <para>Alternatively, specify a JMS Destination object as "defaultResponseDestination".</para>
/// </summary>
/// <value>The name of the default response destination topic.</value>
public string DefaultResponseDestinationTopicName
{
set { defaultResponseDestination = new DestinationNameHolder(value, true); }
}
/// <summary>
/// Gets or sets the destination resolver that should be used to resolve response
/// destination names for this adapter.
/// <para>The default resolver is a <see cref="DynamicDestinationResolver"/>.
/// Specify another implementation, for other strategies, perhaps from a directory service.</para>
/// </summary>
/// <value>The destination resolver.</value>
public IDestinationResolver DestinationResolver
{
get { return destinationResolver; }
@@ -87,28 +181,39 @@ namespace Spring.Messaging.Nms.Listener.Adapter
}
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// <para>The default converter is a {@link SimpleMessageConverter}, which is able
/// to handle BytesMessages}, TextMessages, MapMessages, and ObjectMessages.
/// </para>
/// </remarks>
/// <value>The message converter.</value>
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;
}
/// <summary>
/// Standard JMS {@link MessageListener} entry point.
/// <para>Delegates the message to the target listener method, with appropriate
/// conversion of the message arguments
/// </para>
/// </summary>
/// <remarks>
/// In case of an exception, the <see cref="HandleListenerException"/> method will be invoked.
/// <b>Note</b>
/// Does not support sending response messages based on
/// result objects returned from listener methods. Use the
/// <see cref="ISessionAwareMessageListener"/> entry point (typically through a Spring
/// message listener container) for handling result objects as well.
/// </remarks>
/// <param name="message">The incoming message.</param>
public void OnMessage(IMessage message)
{
try
@@ -121,9 +226,19 @@ namespace Spring.Messaging.Nms.Listener.Adapter
}
}
/// <summary>
/// Spring <see cref="ISessionAwareMessageListener"/> entry point.
/// <para>
/// 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.
/// </para>
/// </summary>
/// <param name="message">The incoming message.</param>
/// <param name="session">The session to operate on.</param>
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)
/// <summary>
/// Initialize the default implementations for the adapter's strategies.
/// </summary>
protected virtual void InitDefaultStrategies()
{
MessageConverter = new SimpleMessageConverter();
}
/// <summary>
/// Handle the given exception that arose during listener execution.
/// The default implementation logs the exception at error level.
/// <para>This method only applies when used as standard NMS MessageListener.
/// In case of the Spring <see cref="ISessionAwareMessageListener"/> mechanism,
/// exceptions get handled by the caller instead.
/// </para>
/// </summary>
/// <param name="ex">The exception to handle.</param>
protected virtual void HandleListenerException(Exception ex)
{
logger.Error("Listener execution failed", ex);
}
/// <summary>
/// Extract the message body from the given message.
/// </summary>
/// <param name="message">The message.</param>
/// <returns>the content of the message, to be passed into the
/// listener method as argument</returns>
/// <exception cref="NMSException">if thrown by NMS API methods</exception>
private object ExtractMessage(IMessage message)
{
IMessageConverter converter = MessageConverter;
if (converter != null)
{
return converter.FromMessage(message);
}
return message;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="originalIMessage">The NMS request message.</param>
/// <param name="extractedMessage">The converted JMS request message,
/// to be passed into the listener method as argument.</param>
/// <returns>the name of the listener method (never <code>null</code>)</returns>
/// <exception cref="NMSException">if thrown by NMS API methods</exception>
protected virtual string GetHandlerMethodName(IMessage originalIMessage, object extractedMessage)
{
return DefaultHandlerMethod;
}
/// <summary>
/// Handles the given result object returned from the listener method, sending a response message back.
/// </summary>
/// <param name="result">The result object to handle (never <code>null</code>).</param>
/// <param name="request">The original request message.</param>
/// <param name="session">The session to operate on (may be <code>null</code>).</param>
protected virtual void HandleResult(object result, IMessage request, ISession session)
{
if (session != null)
{
@@ -170,6 +346,14 @@ namespace Spring.Messaging.Nms.Listener.Adapter
}
}
/// <summary>
/// Builds a JMS message to be sent as response based on the given result object.
/// </summary>
/// <param name="session">The JMS Session to operate on.</param>
/// <param name="result">The content of the message, as returned from the listener method.</param>
/// <returns>the JMS <code>Message</code> (never <code>null</code>)</returns>
/// <exception cref="MessageConversionException">If there was an error in message conversion</exception>
/// <exception cref="NMSException">if thrown by NMS API methods</exception>
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;
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="request">The original incoming message.</param>
/// <param name="response">The outgoing JMS message about to be sent.</param>
/// <exception cref="NMSException">if thrown by NMS API methods</exception>
protected virtual void PostProcessResponse(IMessage request, IMessage response)
{
response.NMSCorrelationID = request.NMSCorrelationID;
}
/// <summary>
/// Determine a response destination for the given message.
/// </summary>
/// <remarks>
/// <para>The default implementation first checks the JMS Reply-To
/// Destination of the supplied request; if that is not <code>null</code>
/// it is returned; if it is <code>null</code>, then the configured
/// <see cref="ResolveDefaultResponseDestination"/> default response destination}
/// is returned; if this too is <code>null</code>, then an
/// <see cref="InvalidDestinationException"/>is thrown.
/// </para>
/// </remarks>
/// <param name="request">The original incoming message.</param>
/// <param name="response">Tthe outgoing message about to be sent.</param>
/// <param name="session">The session to operate on.</param>
/// <returns>the response destination (never <code>null</code>)</returns>
/// <exception cref="NMSException">if thrown by NMS API methods</exception>
/// <exception cref="InvalidDestinationException">if no destination can be determined.</exception>
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;
}
/// <summary>
/// Resolves the default response destination into a Destination, using this
/// accessor's <see cref="IDestinationResolver"/> in case of a destination name.
/// </summary>
/// <param name="session">The session to operate on.</param>
/// <returns>The located destination</returns>
protected virtual IDestination ResolveDefaultResponseDestination(ISession session)
{
IDestination dest = defaultResponseDestination as IDestination;
@@ -225,7 +440,13 @@ namespace Spring.Messaging.Nms.Listener.Adapter
return null;
}
/// <summary>
/// Sends the given response message to the given destination.
/// </summary>
/// <param name="session">The session to operate on.</param>
/// <param name="destination">The destination to send to.</param>
/// <param name="response">The outgoing message about to be sent.</param>
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);
}
}
/// <summary>
/// Post-process the given message producer before using it to send the response.
/// The default implementation is empty.
/// </summary>
/// <param name="producer">The producer that will be used to send the message.</param>
/// <param name="response">The outgoing message about to be sent.</param>
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;
}
}
/// <summary>
/// Internal class combining a destination name and its target destination type (queue or topic).
/// </summary>
internal class DestinationNameHolder
{
private string name;

View File

@@ -1,6 +0,0 @@
namespace Spring.Messaging.Nms.Listener
{
public class DefaultMessageListenerContainer
{
}
}

View File

@@ -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
{
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// Supported by Spring's <see cref="SimpleMessageListenerContainer"/>
/// as direct alternative to the standard MessageListener interface.
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
public interface ISessionAwareMessageListener
{
/// <summary> 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.
/// </summary>
/// <param name="message">the received NMS message
/// </param>
/// <param name="session">the underlying NMS ISession
/// <param name="session">the underlying NMS Session
/// </param>
/// <throws> NMSException if thrown by NMS methods </throws>
void OnMessage(IMessage message, ISession session);

View File

@@ -19,7 +19,7 @@
#endregion
using Apache.NMS;
using Spring.Messaging.Nms.Connection;
using Spring.Messaging.Nms.Connections;
namespace Spring.Messaging.Nms.Listener
{

View File

@@ -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
{
/// <summary>
/// Message listener container that uses the plain NMS client API's
/// <see cref="IMessageConsumer.Listener"/> method to create concurrent
/// MessageConsumer.Listener method to create concurrent
/// MessageConsumers for the specified listeners.
/// </summary>
public class SimpleMessageListenerContainer : AbstractMessageListenerContainer, IExceptionListener
@@ -57,12 +58,31 @@ namespace Spring.Messaging.Nms.Listener
#region Properties
/// <summary>
/// Gets or sets a value indicating whether to inhibit the delivery of messages published by its own connection.
/// Default is "false".
/// </summary>
/// <value><c>true</c> if should inhibit the delivery of messages published by its own connection; otherwise, <c>false</c>.</value>
public bool PubSubNoLocal
{
get { return pubSubNoLocal; }
set { pubSubNoLocal = value; }
}
/// <summary>
/// Specify the number of concurrent consumers to create. Default is 1.
/// </summary>
/// <remarks>
/// 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.
/// <para>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.
/// </para>
/// </remarks>
/// <value>The concurrent consumers.</value>
public int ConcurrentConsumers
{
set
@@ -82,6 +102,10 @@ namespace Spring.Messaging.Nms.Listener
#endregion
/// <summary>
/// Call base class for valdation and then check that if the subscription is durable that the number of
/// concurrent consumers is equal to one.
/// </summary>
protected override void ValidateConfiguration()
{
base.ValidateConfiguration();
@@ -120,10 +144,18 @@ namespace Spring.Messaging.Nms.Listener
{
base.PrepareSharedConnection(connection);
connection.ExceptionListener += OnException;
}
}
/// <summary>
/// <see cref="IExceptionListener"/> implementation, invoked by the NMS provider in
/// case of connection failures. Re-initializes this listener container's
/// shared connection and its sessions and consumers.
/// </summary>
/// <param name="exception">The reported connection exception.</param>
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
/// </summary>
/// <param name="session">The session to work on.</param>
/// <param name="session">The session to work on.</param>
/// <returns>the MessageConsumer"/></returns>
/// <exception cref="NMSException">if thrown by NMS methods</exception>
private IMessageConsumer CreateListenerConsumer(ISession session)
{
@@ -216,6 +248,12 @@ namespace Spring.Messaging.Nms.Listener
}
/// <summary>
/// Creates a MessageConsumer for the given Session and Destination.
/// </summary>
/// <param name="session">The session to create a MessageConsumer for.</param>
/// <param name="destination">The destination to create a MessageConsumer for.</param>
/// <returns>The new MessageConsumer</returns>
protected IMessageConsumer CreateConsumer(ISession session, IDestination destination)
{
// Only pass in the NoLocal flag in case of a Topic:

View File

@@ -25,10 +25,10 @@ namespace Spring.Messaging.Nms
/// <summary>
/// Delegate that creates a NMS message given a ISession
/// </summary>
/// <param name="session">the NMS ISession to be used to create the
/// <code>IMessage</code> (never <code>null</code>)
/// <param name="session">the NMS Session to be used to create the
/// <code>Message</code> (never <code>null</code>)
/// </param>
/// <returns> the <code>IMessage</code> to be sent
/// <returns> the <code>Message</code> to be sent
/// </returns>
/// <throws>NMSException if thrown by NMS API methods </throws>
public delegate IMessage IMessageCreatorDelegate(ISession session);

View File

@@ -29,9 +29,9 @@ namespace Spring.Messaging.Nms
/// Convenient super class for application classes that need NMS access.
/// </summary>
/// <remarks>
/// 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 <code>createNmsTemplate</code> method.
///
/// </remarks>
@@ -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
/// <summary>
/// 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.
/// </summary>
/// <value>The connection factory.</value>
public IConnectionFactory ConnectionFactory
@@ -75,9 +75,9 @@ namespace Spring.Messaging.Nms
}
/// <summary>
/// Creates a NmsTemplate for the given IConnectionFactory.
/// Creates a NmsTemplate for the given ConnectionFactory.
/// </summary>
/// <remarks>Only invoked if populating the gateway with a IConnectionFactory reference.
/// <remarks>Only invoked if populating the gateway with a ConnectionFactory reference.
/// Can be overridden in subclasses to provide a different NmsTemplate instance
/// </remarks>
///
@@ -88,6 +88,9 @@ namespace Spring.Messaging.Nms
return new NmsTemplate(connectionFactory);
}
/// <summary>
/// Ensures that the JmsTemplate is specified and calls <see cref="InitGateway"/>.
/// </summary>
public void AfterPropertiesSet()
{
if (jmsTemplate == null)

View File

@@ -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.</para>
///
/// <para>Default settings for NMS ISessions are "not transacted" and "auto-acknowledge".</para>
/// <para>Default settings for NMS Sessions is "auto-acknowledge".</para>
///
/// <para>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
/// <summary>
/// Timeout value indicating that a receive operation should
/// check if a message is immediately available without blocking.
/// </summary>
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
/// <summary> Create a new NmsTemplate.</summary>
/// <remarks>
/// <para>Note: The IConnectionFactory has to be set before using the instance.
/// <para>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.</para>
/// typically setting the ConnectionFactory.</para>
/// </remarks>
public NmsTemplate()
{
@@ -103,8 +105,8 @@ namespace Spring.Messaging.Nms
}
/// <summary> Create a new NmsTemplate, given a IConnectionFactory.</summary>
/// <param name="connectionFactory">the IConnectionFactory to obtain IConnections from
/// <summary> Create a new NmsTemplate, given a ConnectionFactory.</summary>
/// <param name="connectionFactory">the ConnectionFactory to obtain IConnections from
/// </param>
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
}
/// <summary> Execute the action specified by the given action object within a
/// NMS ISession.
/// NMS Session.
/// </summary>
/// <remarks> Generalized version of <code>execute(ISessionCallback)</code>,
/// allowing the NMS IConnection to be started on the fly.
/// allowing the NMS Connection to be started on the fly.
/// <p>Use <code>execute(ISessionCallback)</code> 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 <code>receive</code> methods.</p>
/// </remarks>
/// <param name="action">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..
/// <summary>
/// Gets or sets a value indicating whether IMessageIds are.
/// Gets or sets a value indicating whether Message Ids are enabled.
/// </summary>
/// <value><c>true</c> if [message id enabled]; otherwise, <c>false</c>.</value>
/// <value><c>true</c> if message id enabled; otherwise, <c>false</c>.</value>
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.
/// <summary>
/// Gets or sets a value indicating whether message timestamps are enabled.
/// </summary>
@@ -326,9 +326,6 @@ namespace Spring.Messaging.Nms
set { persistent = value; }
}
//TODO verify admin...
/// <summary>
/// Gets or sets the priority when sending.
/// </summary>
@@ -358,6 +355,11 @@ namespace Spring.Messaging.Nms
#endregion
/// <summary>
/// Extract the content from the given JMS message.
/// </summary>
/// <param name="message">The Message to convert (can be <code>null</code>).</param>
/// <returns>The content of the message, or <code>null</code> if none</returns>
protected virtual object DoConvertFromMessage(IMessage message)
{
if (message != null)
@@ -369,7 +371,7 @@ namespace Spring.Messaging.Nms
#region NMS Factory Methods
/// <summary> Fetch an appropriate IConnection from the given NmsResourceHolder.
/// <summary> Fetch an appropriate Connection from the given NmsResourceHolder.
/// </summary>
/// <param name="holder">the NmsResourceHolder
/// </param>
@@ -381,7 +383,7 @@ namespace Spring.Messaging.Nms
return holder.GetConnection();
}
/// <summary> Fetch an appropriate ISession from the given NmsResourceHolder.
/// <summary> Fetch an appropriate Session from the given NmsResourceHolder.
/// </summary>
/// <param name="holder">the NmsResourceHolder
/// </param>
@@ -393,16 +395,16 @@ namespace Spring.Messaging.Nms
return holder.GetSession();
}
/// <summary> Create a NMS IMessageProducer for the given ISession and IDestination,
/// <summary> Create a NMS MessageProducer for the given Session and Destination,
/// configuring it to disable message ids and/or timestamps (if necessary).
/// <p>Delegates to <code>doCreateProducer</code> for creation of the raw
/// NMS IMessageProducer, which needs to be specific to NMS 1.1 or 1.0.2.</p>
/// NMS MessageProducer</p>
/// </summary>
/// <param name="session">the NMS ISession to create a IMessageProducer for
/// <param name="session">the NMS Session to create a MessageProducer for
/// </param>
/// <param name="destination">the NMS IDestination to create a IMessageProducer for
/// <param name="destination">the NMS Destination to create a MessageProducer for
/// </param>
/// <returns> the new NMS IMessageProducer
/// <returns> the new NMS MessageProducer
/// </returns>
/// <throws> NMSException if thrown by NMS API methods </throws>
/// <seealso cref="DoCreateProducer">
@@ -426,14 +428,33 @@ namespace Spring.Messaging.Nms
}
/// <summary> Create a raw NMS IMessageProducer for the given ISession and IDestination.
/// <p>This implementation uses NMS 1.1 API.</p>
/// <summary>
/// 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.
/// </summary>
/// <param name="session">the NMS ISession to create a IMessageProducer for
/// <remarks>
/// 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.
/// </remarks>
/// <param name="session">The session to check.</param>
/// <returns>
/// <c>true</c> if the session is locally transacted; otherwise, <c>false</c>.
/// </returns>
protected virtual bool IsSessionLocallyTransacted(ISession session)
{
return SessionTransacted &&
!ConnectionFactoryUtils.IsSessionTransactional(session, ConnectionFactory);
}
/// <summary> Create a raw NMS MessageProducer for the given Session and Destination.
/// </summary>
/// <param name="session">the NMS Session to create a MessageProducer for
/// </param>
/// <param name="destination">the NMS IDestination to create a IMessageProducer for
/// <param name="destination">the NMS IDestination to create a MessageProducer for
/// </param>
/// <returns> the new NMS IMessageProducer
/// <returns> the new NMS MessageProducer
/// </returns>
/// <throws>NMSException if thrown by NMS API methods </throws>
protected virtual IMessageProducer DoCreateProducer(ISession session, IDestination destination)
@@ -441,12 +462,11 @@ namespace Spring.Messaging.Nms
return session.CreateProducer(destination);
}
/// <summary> Create a NMS IMessageConsumer for the given ISession and IDestination.
/// <p>This implementation uses NMS 1.1 API.</p>
/// <summary> Create a NMS MessageConsumer for the given Session and Destination.
/// </summary>
/// <param name="session">the NMS ISession to create a IMessageConsumer for
/// <param name="session">the NMS Session to create a MessageConsumer for
/// </param>
/// <param name="destination">the NMS IDestination to create a IMessageConsumer for
/// <param name="destination">the NMS Destination to create a MessageConsumer for
/// </param>
/// <param name="messageSelector">the message selector for this consumer (can be <code>null</code>)
/// </param>
@@ -469,14 +489,24 @@ namespace Spring.Messaging.Nms
}
}
//TODO refactor to not pass null as a 'switch' for behavior.
/// <summary>
/// Send the given message.
/// </summary>
/// <param name="session">The session to operate on.</param>
/// <param name="destination">The destination to send to.</param>
/// <param name="messageCreatorDelegate">The message creator delegate callback to create a Message.</param>
protected internal virtual void DoSend(ISession session, IDestination destination, IMessageCreatorDelegate messageCreatorDelegate)
{
AssertUtils.ArgumentNotNull(messageCreatorDelegate, "IMessageCreatorDelegate must not be null");
DoSend(session, destination, null, messageCreatorDelegate);
}
/// <summary>
/// Send the given message.
/// </summary>
/// <param name="session">The session to operate on.</param>
/// <param name="destination">The destination to send to.</param>
/// <param name="messageCreator">The message creator callback to create a Message.</param>
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
}
/// <summary> Send the given NMS message.</summary>
/// <param name="session">the NMS ISession to operate on
/// <param name="session">the NMS Session to operate on
/// </param>
/// <param name="destination">the NMS IDestination to send to
/// <param name="destination">the NMS Destination to send to
/// </param>
/// <param name="messageCreator">callback to create a NMS IMessage
/// <param name="messageCreator">callback to create a NMS Message
/// </param>
/// <param name="messageCreatorDelegate">delegate callback to create a NMS IMessage
/// <param name="messageCreatorDelegate">delegate callback to create a NMS Message
/// </param>
/// <throws>NMSException if thrown by NMS API methods </throws>
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
/// <summary> Actually send the given NMS message.</summary>
/// <param name="producer">the NMS IMessageProducer to send with
/// <param name="producer">the NMS MessageProducer to send with
/// </param>
/// <param name="message">the NMS IMessage to send
/// <param name="message">the NMS Message to send
/// </param>
/// <throws> NMSException if thrown by NMS API methods </throws>
protected virtual void DoSend(IMessageProducer producer, IMessage message)
@@ -554,10 +584,10 @@ namespace Spring.Messaging.Nms
#region INmsOperations Implementation
/// <summary> Execute the action specified by the given action object within
/// a NMS ISession.
/// <p>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.</p>
/// a NMS Session.
/// <p>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.</p>
/// </summary>
/// <param name="action">callback object that exposes the session
/// </param>
@@ -570,7 +600,7 @@ namespace Spring.Messaging.Nms
}
/// <summary> 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.
/// </summary>
/// <param name="action">callback object that exposes the session/producer pair
@@ -603,7 +633,7 @@ namespace Spring.Messaging.Nms
}
/// <summary> 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.
/// </summary>
/// <param name="destination">the destination to send this message to
/// </param>
@@ -616,7 +646,7 @@ namespace Spring.Messaging.Nms
}
/// <summary> 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.
/// </summary>
/// <param name="destinationName">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
}
/// <summary> 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.
/// </summary>
/// <param name="destination">the destination to send this message to
/// </param>
@@ -663,7 +693,7 @@ namespace Spring.Messaging.Nms
}
/// <summary> 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.
/// </summary>
/// <param name="destinationName">the destination to send this message to
/// </param>
@@ -898,12 +928,25 @@ namespace Spring.Messaging.Nms
return Execute(new ReceiveSelectedCallback(this, destinationName, messageSelector), true) as IMessage;
}
/// <summary>
/// Receive a message.
/// </summary>
/// <param name="session">The session to operate on.</param>
/// <param name="destination">The destination to receive from.</param>
/// <param name="messageSelector">The message selector for this consumer (can be <code>null</code></param>
/// <returns>The Message received, or <code>null</code> if none.</returns>
protected virtual IMessage DoReceive(ISession session, IDestination destination, string messageSelector)
{
return DoReceive(session, CreateConsumer(session, destination, messageSelector));
}
/// <summary>
/// Receive a message.
/// </summary>
/// <param name="session">The session to operate on.</param>
/// <param name="consumer">The consumer to receive with.</param>
/// <returns>The Message received, or <code>null</code> if none</returns>
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
/// <summary>
/// ResourceFactory implementation that delegates to this template's callback methods.
/// </summary>
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; }
}
}

View File

@@ -31,25 +31,25 @@ namespace Spring.Messaging.Nms.Support.Converter
/// <author>Mark Pollack (.NET)</author>
public interface IMessageConverter
{
/// <summary> Convert a .NET object to a NMS IMessage using the supplied session
/// <summary> Convert a .NET object to a NMS Message using the supplied session
/// to create the message object.
/// </summary>
/// <param name="objectToConvert">the object to convert
/// </param>
/// <param name="session">the ISession to use for creating a NMS IMessage
/// <param name="session">the Session to use for creating a NMS Message
/// </param>
/// <returns> the NMS IMessage
/// <returns> the NMS Message
/// </returns>
/// <throws>NMSException if thrown by NMS API methods </throws>
/// <throws>IMessageConversionException in case of conversion failure </throws>
/// <throws>MessageConversionException in case of conversion failure </throws>
IMessage ToMessage(object objectToConvert, ISession session);
/// <summary> Convert from a NMS IMessage to a .NET object.</summary>
/// <summary> Convert from a NMS Message to a .NET object.</summary>
/// <param name="message">the message to convert
/// </param>
/// <returns> the converted .NET object
/// </returns>
/// <throws>IMessageConversionException in case of conversion failure </throws>
/// <throws>MessageConversionException in case of conversion failure </throws>
object FromMessage(IMessage message);
}
}

View File

@@ -19,24 +19,18 @@
#endregion
using System;
using System.Runtime.Serialization;
using Apache.NMS;
namespace Spring.Messaging.Nms.Support.Converter
{
/// <summary> Thrown by IMessageConverter implementations when the conversion
/// of an object to/from a IMessage fails.
///
/// of an object to/from a Message fails.
/// </summary>
/// <author>Mark Pollack</author>
[Serializable]
public class IMessageConversionException : ApplicationException
public class MessageConversionException : NMSException
{
//TODO add jms root exception hierarchy?....
#region Constructor (s) / Destructor
/// <summary>Creates a new instance of the IMessageConverterException class.</summary>
public IMessageConversionException()
{
}
/// <summary>
/// Creates a new instance of the IMessageConverterException class. with the specified message.
@@ -44,7 +38,7 @@ namespace Spring.Messaging.Nms.Support.Converter
/// <param name="message">
/// A message about the exception.
/// </param>
public IMessageConversionException(string message)
public MessageConversionException(string message)
: base(message)
{
}
@@ -59,27 +53,11 @@ namespace Spring.Messaging.Nms.Support.Converter
/// <param name="rootCause">
/// The root exception that is being wrapped.
/// </param>
public IMessageConversionException(string message, Exception rootCause)
public MessageConversionException(string message, Exception rootCause)
: base(message, rootCause)
{
}
/// <summary>
/// Creates a new instance of the IMessageConverterException class.
/// </summary>
/// <param name="info">
/// The <see cref="System.Runtime.Serialization.SerializationInfo"/>
/// that holds the serialized object data about the exception being thrown.
/// </param>
/// <param name="context">
/// The <see cref="System.Runtime.Serialization.StreamingContext"/>
/// that contains contextual information about the source or destination.
/// </param>
protected IMessageConversionException(
SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
#endregion
}
}

View File

@@ -39,17 +39,17 @@ namespace Spring.Messaging.Nms.Support.Converter
/// <author>Mark Pollack (.NET)</author>
public class SimpleMessageConverter : IMessageConverter
{
/// <summary> Convert a .NET object to a NMS IMessage using the supplied session
/// <summary> Convert a .NET object to a NMS Message using the supplied session
/// to create the message object.
/// </summary>
/// <param name="objectToConvert">the object to convert
/// </param>
/// <param name="session">the ISession to use for creating a NMS IMessage
/// <param name="session">the Session to use for creating a NMS Message
/// </param>
/// <returns> the NMS IMessage
/// <returns> the NMS Message
/// </returns>
/// <throws>NMSException if thrown by NMS API methods </throws>
/// <throws>IMessageConversionException in case of conversion failure </throws>
/// <throws>MessageConversionException in case of conversion failure </throws>
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");
}
}
/// <summary> Convert from a NMS IMessage to a .NET object.</summary>
/// <summary> Convert from a NMS Message to a .NET object.</summary>
/// <param name="message">the message to convert
/// </param>
/// <returns> the converted .NET object
/// </returns>
/// <throws>IMessageConversionException in case of conversion failure </throws>
/// <throws>MessageConversionException in case of conversion failure </throws>
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
/// </param>
/// <returns> the resulting Map
/// </returns>
/// <throws> NMSException if thrown by NMS methods </throws>
/// <throws>NMSException if thrown by NMS methods </throws>
protected virtual IDictionary ExtractMapFromMessage(IMapMessage message)
{
IDictionary dictionary = new Hashtable();
@@ -222,6 +222,11 @@ namespace Spring.Messaging.Nms.Support.Converter
return dictionary;
}
/// <summary>
/// Extracts the serializable object from the given object message.
/// </summary>
/// <param name="message">The message to convert.</param>
/// <returns>The resulting serializable object.</returns>
protected virtual object ExtractSerializableFromMessage(
IObjectMessage message)
{

View File

@@ -33,7 +33,7 @@ namespace Spring.Messaging.Nms.Support.IDestinations
/// <summary> Resolve the given destination name, either as located resource
/// or as dynamic destination.
/// </summary>
/// <param name="session">the current NMS ISession
/// <param name="session">the current NMS Session
/// </param>
/// <param name="destinationName">the name of the destination
/// </param>
@@ -58,7 +58,7 @@ namespace Spring.Messaging.Nms.Support.IDestinations
/// <summary> Resolve the given destination name to a Topic.</summary>
/// <param name="session">the current NMS ISession
/// <param name="session">the current NMS Session
/// </param>
/// <param name="topicName">the name of the desired Topic.
/// </param>
@@ -70,6 +70,14 @@ namespace Spring.Messaging.Nms.Support.IDestinations
return session.GetTopic(topicName);
}
/// <summary> Resolve the given destination name to a Queue.</summary>
/// <param name="session">the current NMS Session
/// </param>
/// <param name="queueName">the name of the desired Queue.
/// </param>
/// <returns> the NMS Queue name
/// </returns>
/// <throws>NMSException if resolution failed </throws>
protected internal virtual IDestination ResolveQueue(ISession session, string queueName)
{
return session.GetQueue(queueName);

View File

@@ -43,7 +43,7 @@ namespace Spring.Messaging.Nms.Support.IDestinations
/// <summary> Resolve the given destination name, either as located resource
/// or as dynamic destination.
/// </summary>
/// <param name="session">the current NMS ISession
/// <param name="session">the current NMS Session
/// </param>
/// <param name="destinationName">the name of the destination
/// </param>

View File

@@ -25,14 +25,12 @@ using Apache.NMS;
namespace Spring.Messaging.Nms.Support
{
/// <summary> Base class for NmsTemplate and other
/// NMS-accessing gateway helpers</summary>
/// <remarks>It defines common properties like the
/// IConnectionFactory}. The subclass
/// NmsIDestinationAccessor adds
/// further, destination-related properties.
///
/// <summary> Base class for NmsTemplate and other NMS-accessing gateway helpers</summary>
/// <remarks>It defines common properties like the ConnectionFactory}. The subclass
/// NmsIDestinationAccessor adds further, destination-related properties.
/// <para>
/// Not intended to be used directly. See NmsTemplate.
/// </para>
/// </remarks>
/// <author>Juergen Hoeller</author>
/// <author>Mark Pollack (.NET)</author>
@@ -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
/// <summary>
/// Gets or sets the connection factory to use for obtaining NMS IConnections.
/// Gets or sets the connection factory to use for obtaining NMS Connections.
/// </summary>
/// <value>The connection factory.</value>
virtual public IConnectionFactory ConnectionFactory
@@ -78,8 +76,7 @@ namespace Spring.Messaging.Nms.Support
/// </summary>
/// <remarks>
/// Set the NMS acknowledgement mode that is used when creating a NMS
/// ISession to send a message. The default is ISession.AUTO_ACKNOWLEDGE.
/// <p>Vendor-specific extensions to the acknowledgment mode can be set here as well.</p>
/// Session to send a message. The default is AUTO_ACKNOWLEDGE.
/// </remarks>
/// <value>The session acknowledge mode.</value>
virtual public AcknowledgementMode SessionAcknowledgeMode
@@ -112,7 +109,10 @@ namespace Spring.Messaging.Nms.Support
/// </remarks>
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
/// <summary>
/// Verify that ConnectionFactory property has been set.
/// </summary>
public virtual void AfterPropertiesSet()
{
if (ConnectionFactory == null)
@@ -142,6 +145,11 @@ namespace Spring.Messaging.Nms.Support
return ConnectionFactory.CreateConnection();
}
/// <summary>
/// Creates the session for the given Connection
/// </summary>
/// <param name="con">The connection to create a session for.</param>
/// <returns>The new session</returns>
protected virtual ISession CreateSession(IConnection con)
{
return con.CreateSession(SessionAcknowledgeMode);
@@ -150,9 +158,9 @@ namespace Spring.Messaging.Nms.Support
/// <summary>
/// Returns whether the ISession is in client acknowledgement mode.
/// </summary>
/// <param name="session">The session.</param>
/// <returns>true ifin client ack mode, false otherwise</returns>
protected virtual bool ClientAcknowledge(ISession session)
/// <param name="session">The session to check.</param>
/// <returns>true if in client ack mode, false otherwise</returns>
protected virtual bool IsClientAcknowledge(ISession session)
{
return (session.AcknowledgementMode == AcknowledgementMode.ClientAcknowledge);
}

View File

@@ -25,6 +25,10 @@ using Apache.NMS;
namespace Spring.Messaging.Nms.Support
{
/// <summary>
/// Generic utility methods for working with NMS. Mainly for internal use
/// within the framework, but also useful for custom NMS access code.
/// </summary>
public abstract class NmsUtils
{
#region Logging
@@ -33,20 +37,20 @@ namespace Spring.Messaging.Nms.Support
#endregion
/// <summary> Close the given NMS IConnection and ignore any thrown exception.
/// <summary> Close the given NMS Connection and ignore any thrown exception.
/// This is useful for typical <code>finally</code> blocks in manual NMS code.
/// </summary>
/// <param name="con">the NMS IConnection to close (may be <code>null</code>)
/// <param name="con">the NMS Connection to close (may be <code>null</code>)
/// </param>
public static void CloseConnection(IConnection con)
{
CloseConnection(con, false);
}
/// <summary> Close the given NMS IConnection and ignore any thrown exception.
/// <summary> Close the given NMS Connection and ignore any thrown exception.
/// This is useful for typical <code>finally</code> blocks in manual NMS code.
/// </summary>
/// <param name="con">the NMS IConnection to close (may be <code>null</code>)
/// <param name="con">the NMS Connection to close (may be <code>null</code>)
/// </param>
/// <param name="stop">whether to call <code>stop()</code> before closing
/// </param>
@@ -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);
}
}
}
/// <summary> Close the given NMS ISession and ignore any thrown exception.
/// <summary> Close the given NMS Session and ignore any thrown exception.
/// This is useful for typical <code>finally</code> blocks in manual NMS code.
/// </summary>
/// <param name="session">the NMS ISession to close (may be <code>null</code>)
/// <param name="session">the NMS Session to close (may be <code>null</code>)
/// </param>
public static void CloseSession(ISession session)
{
@@ -109,10 +113,10 @@ namespace Spring.Messaging.Nms.Support
}
}
/// <summary> Close the given NMS IMessageProducer and ignore any thrown exception.
/// <summary> Close the given NMS MessageProducer and ignore any thrown exception.
/// This is useful for typical <code>finally</code> blocks in manual NMS code.
/// </summary>
/// <param name="producer">the NMS IMessageProducer to close (may be <code>null</code>)
/// <param name="producer">the NMS MessageProducer to close (may be <code>null</code>)
/// </param>
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);
}
}
}
/// <summary> Close the given NMS IMessageConsumer and ignore any thrown exception.
/// <summary> Close the given NMS MessageConsumer and ignore any thrown exception.
/// This is useful for typical <code>finally</code> blocks in manual NMS code.
/// </summary>
/// <param name="consumer">the NMS IMessageConsumer to close (may be <code>null</code>)
/// <param name="consumer">the NMS MessageConsumer to close (may be <code>null</code>)
/// </param>
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
// }
/// <summary> Commit the ISession if not within a distributed transaction.</summary>
/// <remarks>Needs investigation - no distributed tx in EMS</remarks>
/// <param name="session">the NMS ISession to commit
/// <summary> Commit the Session if not within a distributed transaction.</summary>
/// <remarks>Needs investigation - no distributed tx in .NET messaging providers</remarks>
/// <param name="session">the NMS Session to commit
/// </param>
/// <throws> NMSException if committing failed </throws>
/// <throws>NMSException if committing failed </throws>
public static void CommitIfNecessary(ISession session)
{
AssertUtils.ArgumentNotNull(session, "ISession must not be null");
@@ -212,9 +216,9 @@ namespace Spring.Messaging.Nms.Support
// }
}
/// <summary> Rollback the ISession if not within a distributed transaction.</summary>
/// <summary> Rollback the Session if not within a distributed transaction.</summary>
/// <remarks>Needs investigation - no distributed tx in EMS</remarks>
/// <param name="session">the NMS ISession to rollback
/// <param name="session">the NMS Session to rollback
/// </param>
/// <throws> NMSException if committing failed </throws>
public static void RollbackIfNecessary(ISession session)

View File

@@ -19,6 +19,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>..\..\..\build\VS.NET.2005\Spring.Messaging.Nms\Debug\Spring.Messaging.Nms.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -67,7 +68,6 @@
<Compile Include="Messaging\Nms\Listener\AbstractMessageListenerContainer.cs" />
<Compile Include="Messaging\Nms\Listener\AbstractNmsListeningContainer.cs" />
<Compile Include="Messaging\Nms\Listener\Adapter\MessageListenerAdapter.cs" />
<Compile Include="Messaging\Nms\Listener\DefaultMessageListenerContainer.cs" />
<Compile Include="Messaging\Nms\Listener\ISessionAwareMessageListener.cs" />
<Compile Include="Messaging\Nms\Listener\LocallyExposedNmsResourceHolder.cs" />
<Compile Include="Messaging\Nms\Listener\SimpleMessageListenerContainer.cs" />