diff --git a/src/Spring/Spring.Core/Spring.Core.2008.csproj b/src/Spring/Spring.Core/Spring.Core.2008.csproj index c4f2062f..519d34d6 100644 --- a/src/Spring/Spring.Core/Spring.Core.2008.csproj +++ b/src/Spring/Spring.Core/Spring.Core.2008.csproj @@ -1,7 +1,7 @@  Local - 9.0.30729 + 9.0.21022 2.0 {710961A3-0DF4-49E4-A26E-F5B9C044AC84} Debug @@ -189,9 +189,7 @@ Code - - Code - + Code diff --git a/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueMetadata.cs b/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueMetadata.cs new file mode 100644 index 00000000..6317555e --- /dev/null +++ b/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueMetadata.cs @@ -0,0 +1,70 @@ +#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 + +namespace Spring.Messaging.Core +{ + /// + /// Encapsulates additional metadata information about the MessageQueue that can not be easily obtained + /// from the MessageQueue itself. + /// + public class MessageQueueMetadata + { + private bool remoteQueue; + + private bool remoteQueueIsTransactional; + + /// + /// Initializes a new instance of the class. + /// + /// if set to true [remote queue]. + /// if set to true [remote queue is transactional]. + public MessageQueueMetadata(bool remoteQueue, bool remoteQueueIsTransactional) + { + this.remoteQueue = remoteQueue; + this.remoteQueueIsTransactional = remoteQueueIsTransactional; + } + + /// + /// Gets or sets a value indicating whether the queue is a remote queue. + /// + /// + /// The operations that one can perform on the MessageQueue depend on if it is local or remote, for + /// example checking if it is transactional. This is very difficult to determine programmatically. + /// The property was made virtual so it can be overridden to take into account custom heuristics you + /// may want to use to determine this programmatically. + /// + /// true if remote queue; otherwise, false. + public virtual bool RemoteQueue + { + get { return remoteQueue; } + } + + /// + /// Gets or sets a value indicating whether the remote queue is transactional. + /// + /// + /// true if the remote queue is transactional; otherwise, false. + /// + public virtual bool RemoteQueueIsTransactional + { + get { return remoteQueueIsTransactional; } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueMetadataCache.cs b/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueMetadataCache.cs new file mode 100644 index 00000000..7ac559f0 --- /dev/null +++ b/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueMetadataCache.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections; +using Common.Logging; +using Spring.Context; +using Spring.Messaging.Support; +using Spring.Objects.Factory; + +namespace Spring.Messaging.Core +{ + public class MessageQueueMetadataCache : IApplicationContextAware, IInitializingObject + { + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof(MessageQueueMetadataCache)); + + #endregion + + private readonly IDictionary itemStore = new Hashtable(); + + private IConfigurableApplicationContext configurableApplicationContext; + private IApplicationContext applicationContext; + + + private bool isInitialized; + + public MessageQueueMetadataCache() + { + } + + public MessageQueueMetadataCache(IConfigurableApplicationContext configurableApplicationContext) + { + this.configurableApplicationContext = configurableApplicationContext; + } + + public IApplicationContext ApplicationContext + { + set { applicationContext = value; } + } + + public void Initialize() + { + IDictionary messageQueueDictionary = configurableApplicationContext.GetObjectsOfType(typeof(MessageQueueFactoryObject)); + lock (itemStore.SyncRoot) + { + foreach (DictionaryEntry entry in messageQueueDictionary) + { + MessageQueueFactoryObject mqfo = entry.Value as MessageQueueFactoryObject; + if (mqfo != null) + { + if (mqfo.Path != null) + { + Insert(mqfo.Path, + new MessageQueueMetadata(mqfo.RemoteQueue, mqfo.RemoteQueueIsTransactional)); + } else + { + #region Logging + if (LOG.IsWarnEnabled) + { + LOG.Warn( + "Path for MessageQueueFactoryObject named [" + + mqfo.ObjectName + "] is null, so can't cache its metadata."); + } + #endregion + } + } else + { + // This would indicate some bug in GetObjectsOfType + LOG.Error("Unexpected type of " + entry.Value.GetType() + " was given as candidate for caching MessageQueueMetadata."); + } + } + isInitialized = true; + } + } + + public void AfterPropertiesSet() + { + IConfigurableApplicationContext ctx = applicationContext as IConfigurableApplicationContext; + if (ctx == null) + { + throw new InvalidOperationException( + "Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); + } + configurableApplicationContext = ctx; + } + + /// + /// Gets the number of items in the cache. + /// + public int Count + { + get + { + lock (itemStore.SyncRoot) + { + return itemStore.Count; + } + } + } + + public bool Initalized + { + get + { + lock (itemStore.SyncRoot) + { + return isInitialized; + } + } + } + /// + /// Gets a collection of all cache queue paths. + /// + public string[] Paths + { + get + { + lock (itemStore.SyncRoot) + { + string[] paths = new string[itemStore.Count]; + int count = 0; + foreach (object path in itemStore.Keys) + { + paths[count] = (string) path; + count++; + } + return paths; + } + } + } + + /// + /// Retrieves MessageQueueMetadata from the cache. + /// + /// The queue path. + /// + /// Item for the specified , or null. + /// + public MessageQueueMetadata Get(string queuePath) + { + lock (itemStore.SyncRoot) + { + return (MessageQueueMetadata) itemStore[queuePath]; + } + } + + /// + /// Removes the specified queue path from the cache + /// + /// The queue path. + public void Remove(string queuePath) + { + lock (itemStore.SyncRoot) + { + itemStore.Remove(queuePath); + } + } + + /// + /// Removes collection of MessageQueueMetaCache from the cache. + /// + /// + /// Array of MessageQueue paths to remove. + /// + public void RemoveAll(string[] queuePaths) + { + lock (itemStore.SyncRoot) + { + foreach (string queuePath in queuePaths) + { + itemStore.Remove(queuePath); + } + } + } + + /// + /// Removes all MessageQueueMetadata from the cache. + /// + public void Clear() + { + lock (itemStore.SyncRoot) + { + itemStore.Clear(); + } + } + + public void Insert(string path, MessageQueueMetadata messageQueueMetadata) + { + lock (itemStore.SyncRoot) + { + itemStore[path] = messageQueueMetadata; + } + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueTemplate.cs b/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueTemplate.cs index c3fb88fa..22baa95e 100644 --- a/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueTemplate.cs +++ b/src/Spring/Spring.Messaging/Messaging/Core/MessageQueueTemplate.cs @@ -19,12 +19,14 @@ #endregion using System; +using System.Collections; using System.Messaging; using Common.Logging; using Spring.Context; using Spring.Messaging.Support; using Spring.Messaging.Support.Converters; using Spring.Objects.Factory; +using Spring.Objects.Factory.Config; namespace Spring.Messaging.Core { @@ -80,10 +82,12 @@ namespace Spring.Messaging.Core private string messageConverterObjectName; private IMessageQueueFactory messageQueueFactory; - private IApplicationContext applicationContext; + protected IApplicationContext applicationContext; private TimeSpan timeout = MessageQueue.InfiniteTimeout; + private MessageQueueMetadataCache metadataCache; + #endregion #region Constructors @@ -194,6 +198,16 @@ namespace Spring.Messaging.Core set { timeout = value; } } + /// + /// Gets or sets the metadata cache. + /// + /// The metadata cache. + public MessageQueueMetadataCache MetadataCache + { + get { return metadataCache; } + set { metadataCache = value; } + } + #endregion #region IApplicationContextAware Members @@ -263,6 +277,14 @@ namespace Spring.Messaging.Core { messageConverterObjectName = QueueUtils.RegisterDefaultMessageConverter(applicationContext); } + //If it has not been set by the user explicitly, then initialize. + if (metadataCache == null) + { + metadataCache = new MessageQueueMetadataCache(); + metadataCache.ApplicationContext = ApplicationContext; + metadataCache.AfterPropertiesSet(); + metadataCache.Initialize(); + } } #endregion @@ -432,14 +454,14 @@ namespace Spring.Messaging.Core /// Sends the message to the given message queue. /// /// If System.Transactions.Transaction.Current is null, then send based on - /// the transaction semantics of the queue definition. See + /// the transaction semantics of the queue definition. See /// The message queue. /// The message. protected virtual void DoSend(MessageQueue messageQueue, Message message) { if (System.Transactions.Transaction.Current == null) { - DoSendMessageQueueTransactional(messageQueue, message); + DoSendMessageQueue(messageQueue, message); } else { @@ -475,52 +497,89 @@ namespace Spring.Messaging.Core /// /// The mq. /// The MSG. - protected virtual void DoSendMessageQueueTransactional(MessageQueue mq, Message msg) + protected virtual void DoSendMessageQueue(MessageQueue mq, Message msg) { MessageQueueTransaction transactionToUse = QueueUtils.GetMessageQueueTransaction(null); + MessageQueueMetadata mqMetadata = metadataCache.Get(mq.Path); + if (mqMetadata != null) + { + if (mqMetadata.RemoteQueue) + { + if (mqMetadata.RemoteQueueIsTransactional) + { + // DefaultMessageQueue transaction is externally managed. + DoSendMessageTransaction(mq, transactionToUse, msg); + return; + } + DoSendMessageQueueNonTransactional(mq, transactionToUse, msg); + return; + } + } + // Handle assuming these are local queues. + if (mq.Transactional) { // DefaultMessageQueue transaction is externally managed. - if (transactionToUse != null) + DoSendMessageTransaction(mq, transactionToUse, msg); + } + else + { + DoSendMessageQueueNonTransactional(mq, transactionToUse, msg); + } + } + + /// + /// Does the send message transaction. + /// + /// The mq. + /// The transaction to use. + /// The MSG. + protected virtual void DoSendMessageTransaction(MessageQueue mq, MessageQueueTransaction transactionToUse, Message msg) + { + if (transactionToUse != null) + { + if (LOG.IsDebugEnabled) { - if (LOG.IsDebugEnabled) - { - LOG.Debug( - "Sending messsage using externally managed MessageQueueTransction to transactional queue [" + - mq.QueueName + "]."); - } - mq.Send(msg, transactionToUse); + LOG.Debug( + "Sending messsage using externally managed MessageQueueTransction to transactional queue with path [" + mq.Path + "]."); } - else - { - /* From MSDN documentation + mq.Send(msg, transactionToUse); + } + else + { + /* From MSDN documentation * If a non-transactional message is sent to a transactional queue, * this component creates a single-message transaction for it, * except in the case of referencing a queue on a remote computer * using a direct format name. In this situation, if you do not specify a * transaction context when sending a message, one is not created for you * and the message will be sent to the dead-letter queue.*/ - LOG.Warn("Sending message using implicit single-message transaction to transactional queue [" + - mq.QueueName + "]."); - mq.Send(msg, MessageQueueTransactionType.Single); - } + LOG.Warn("Sending message using implicit single-message transaction to transactional queue queue with path [" + mq.Path + "]."); + mq.Send(msg, MessageQueueTransactionType.Single); + } + } + + /// + /// Does the send message queue non transactional. + /// + /// The mq. + /// The transaction to use. + /// The MSG. + protected virtual void DoSendMessageQueueNonTransactional(MessageQueue mq, MessageQueueTransaction transactionToUse, Message msg) + { + if (transactionToUse != null) + { + LOG.Warn("Thread local message transaction ignored for sending to non-transactional queue with path [" + mq.Path + "]."); + mq.Send(msg); } else { - if (transactionToUse != null) - { - LOG.Warn("Thread local message transaction ignored for sending to non-transactional queue."); - mq.Send(msg); - } - else - { - if (LOG.IsDebugEnabled) - { - LOG.Debug("Sending messsage without MSMQ transaction to non-TX-QUEUE."); - } - //Typical case, non TLS transaction, non-tx queue. - mq.Send(msg); + if (LOG.IsDebugEnabled) + { + LOG.Debug("Sending messsage without MSMQ transaction to non-transactional queue with path [" + mq.Path + "]."); } + //Typical case, non TLS transaction, non-tx queue. + mq.Send(msg); } } diff --git a/src/Spring/Spring.Messaging/Messaging/Listener/AbstractPeekingMessageListenerContainer.cs b/src/Spring/Spring.Messaging/Messaging/Listener/AbstractPeekingMessageListenerContainer.cs index 8dff1932..874c0c58 100644 --- a/src/Spring/Spring.Messaging/Messaging/Listener/AbstractPeekingMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging/Messaging/Listener/AbstractPeekingMessageListenerContainer.cs @@ -352,7 +352,7 @@ namespace Spring.Messaging.Listener catch (Exception ex) { messageReceived = false; - LOG.Error("Error receiving message from DefaultMessageQueue = [" + mq.QueueName + "]", ex); + LOG.Error("Error receiving message from DefaultMessageQueue = [" + mq.Path + "]", ex); } finally { @@ -440,6 +440,18 @@ namespace Spring.Messaging.Listener { } + /// + /// Template method that gets called right before a new message is received, i.e. + /// messageQueue.Receive(). + /// + /// It allows subclasses to modify the state of the MessageQueue + /// before receiving which maybe required when using remote queues, for example + /// to set a MessageFormatter. + /// + protected virtual void BeforeMessageReceived(MessageQueue messageQueue) + { + } + #endregion } } \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Listener/DistributedTxMessageListenerContainer.cs b/src/Spring/Spring.Messaging/Messaging/Listener/DistributedTxMessageListenerContainer.cs index bda5feec..8b480b6c 100644 --- a/src/Spring/Spring.Messaging/Messaging/Listener/DistributedTxMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging/Messaging/Listener/DistributedTxMessageListenerContainer.cs @@ -151,7 +151,7 @@ namespace Spring.Messaging.Listener if (LOG.IsTraceEnabled) { - LOG.Trace("Message recieved is null from Queue = [" + mq.QueueName + "]"); + LOG.Trace("Message recieved is null from Queue = [" + mq.Path + "]"); } #endregion @@ -167,7 +167,7 @@ namespace Spring.Messaging.Listener if (LOG.IsDebugEnabled) { - LOG.Debug("Received message [" + message.Id + "] on queue [" + mq.QueueName + "]"); + LOG.Debug("Received message [" + message.Id + "] on queue [" + mq.Path + "]"); } #endregion diff --git a/src/Spring/Spring.Messaging/Messaging/Listener/MessageListenerAdapter.cs b/src/Spring/Spring.Messaging/Messaging/Listener/MessageListenerAdapter.cs index 8ebab361..3deec1b2 100644 --- a/src/Spring/Spring.Messaging/Messaging/Listener/MessageListenerAdapter.cs +++ b/src/Spring/Spring.Messaging/Messaging/Listener/MessageListenerAdapter.cs @@ -18,7 +18,6 @@ #endregion -using System; using System.Collections; using System.Messaging; using Common.Logging; @@ -28,7 +27,6 @@ using Spring.Messaging.Core; using Spring.Messaging.Support; using Spring.Messaging.Support.Converters; using Spring.Objects.Factory; -using Spring.Objects.Factory.Support; using Spring.Reflection.Dynamic; namespace Spring.Messaging.Listener @@ -451,6 +449,9 @@ namespace Spring.Messaging.Listener IMessageConverter converter = MessageConverter; if (converter != null) { + // This is the default Message converter registered in QueueUtils.RegisterDefaultMessageConverter + // and used by MessageQueueTemplate and the MessageListenerAdapter if no other Message converage is + // set via the property MessageConverteryObjectName. if (messageConverterObjectName.Equals("__XmlMessageConverter__")) { return converter.ToMessage(result.ToString()); diff --git a/src/Spring/Spring.Messaging/Messaging/Listener/NonTransactionalMessageListenerContainer.cs b/src/Spring/Spring.Messaging/Messaging/Listener/NonTransactionalMessageListenerContainer.cs index 03310f30..e2501465 100644 --- a/src/Spring/Spring.Messaging/Messaging/Listener/NonTransactionalMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging/Messaging/Listener/NonTransactionalMessageListenerContainer.cs @@ -86,11 +86,11 @@ namespace Spring.Messaging.Listener if (LOG.IsTraceEnabled) { - LOG.Trace("Receiving message with zero timeout for queue = [" + mq.QueueName + "]"); + LOG.Trace("Receiving message with zero timeout for queue = [" + mq.Path + "]"); } #endregion - + BeforeMessageReceived(mq); message = mq.Receive(TimeSpan.Zero); } catch (MessageQueueException ex) @@ -119,7 +119,7 @@ namespace Spring.Messaging.Listener if (LOG.IsErrorEnabled) { - LOG.Error("Error receiving message from DefaultMessageQueue [" + mq.QueueName + + LOG.Error("Error receiving message from DefaultMessageQueue [" + mq.Path + "], closing queue and clearing connection cache."); } @@ -140,7 +140,7 @@ namespace Spring.Messaging.Listener if (LOG.IsTraceEnabled) { - LOG.Trace("Message recieved is null from Queue = [" + mq.QueueName + "]"); + LOG.Trace("Message recieved is null from Queue = [" + mq.Path + "]"); } #endregion @@ -154,7 +154,7 @@ namespace Spring.Messaging.Listener if (LOG.IsDebugEnabled) { - LOG.Debug("Received message [" + message.Id + "] on queue [" + mq.QueueName + "]"); + LOG.Debug("Received message [" + message.Id + "] on queue [" + mq.Path + "]"); } #endregion diff --git a/src/Spring/Spring.Messaging/Messaging/Listener/SendToQueueDistributedTransactionExceptionHandler.cs b/src/Spring/Spring.Messaging/Messaging/Listener/SendToQueueDistributedTransactionExceptionHandler.cs index 1967d3c0..7fc01ea5 100644 --- a/src/Spring/Spring.Messaging/Messaging/Listener/SendToQueueDistributedTransactionExceptionHandler.cs +++ b/src/Spring/Spring.Messaging/Messaging/Listener/SendToQueueDistributedTransactionExceptionHandler.cs @@ -126,7 +126,7 @@ namespace Spring.Messaging.Listener if (LOG.IsInfoEnabled) { - LOG.Info("Sending message with id = [" + message.Id + "] to queue [" + mq.QueueName + "]."); + LOG.Info("Sending message with id = [" + message.Id + "] to queue [" + mq.Path + "]."); } #endregion @@ -139,7 +139,7 @@ namespace Spring.Messaging.Listener if (LOG.IsErrorEnabled) { - LOG.Error("Could not send message with id = [" + message.Id + "] to queue [" + mq.QueueName + "].", e); + LOG.Error("Could not send message with id = [" + message.Id + "] to queue [" + mq.Path + "].", e); LOG.Error("Message will not be processed. Message Body = " + message.Body); } diff --git a/src/Spring/Spring.Messaging/Messaging/Listener/SendToQueueExceptionHandler.cs b/src/Spring/Spring.Messaging/Messaging/Listener/SendToQueueExceptionHandler.cs index 63d5318f..9975d954 100644 --- a/src/Spring/Spring.Messaging/Messaging/Listener/SendToQueueExceptionHandler.cs +++ b/src/Spring/Spring.Messaging/Messaging/Listener/SendToQueueExceptionHandler.cs @@ -155,7 +155,7 @@ namespace Spring.Messaging.Listener if (LOG.IsInfoEnabled) { - LOG.Info("Sending message with id = [" + message.Id + "] to queue [" + mq.QueueName + "]."); + LOG.Info("Sending message with id = [" + message.Id + "] to queue [" + mq.Path + "]."); } #endregion @@ -169,7 +169,7 @@ namespace Spring.Messaging.Listener if (LOG.IsErrorEnabled) { - LOG.Error("Could not send message with id = [" + message.Id + "] to queue [" + mq.QueueName + "].",e); + LOG.Error("Could not send message with id = [" + message.Id + "] to queue [" + mq.Path + "].",e); LOG.Error("Message will not be processed. Message Body = " + message.Body); } diff --git a/src/Spring/Spring.Messaging/Messaging/Listener/TransactionalMessageListenerContainer.cs b/src/Spring/Spring.Messaging/Messaging/Listener/TransactionalMessageListenerContainer.cs index 1c85145a..af443c36 100644 --- a/src/Spring/Spring.Messaging/Messaging/Listener/TransactionalMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging/Messaging/Listener/TransactionalMessageListenerContainer.cs @@ -327,7 +327,7 @@ namespace Spring.Messaging.Listener if (LOG.IsErrorEnabled) { - LOG.Error("Error receiving message from DefaultMessageQueue [" + mq.QueueName + + LOG.Error("Error receiving message from DefaultMessageQueue [" + mq.Path + "], closing queue and clearing connection cache."); } @@ -350,7 +350,7 @@ namespace Spring.Messaging.Listener if (LOG.IsTraceEnabled) { - LOG.Trace("Message recieved is null from Queue = [" + mq.QueueName + "]"); + LOG.Trace("Message recieved is null from Queue = [" + mq.Path + "]"); } #endregion @@ -365,7 +365,7 @@ namespace Spring.Messaging.Listener if (LOG.IsDebugEnabled) { - LOG.Debug("Received message [" + message.Id + "] on queue [" + mq.QueueName + "]"); + LOG.Debug("Received message [" + message.Id + "] on queue [" + mq.Path + "]"); } #endregion @@ -396,7 +396,7 @@ namespace Spring.Messaging.Listener { LOG.Debug( "Exception handler's TransactionAction has rolled back MessageQueueTransaction for queue [" + - mq.QueueName + "]"); + mq.Path + "]"); } #endregion @@ -442,7 +442,7 @@ namespace Spring.Messaging.Listener if (LOG.IsTraceEnabled) { - LOG.Trace("Started MessageQueueTransaction for queue = [" + mq.QueueName + "]"); + LOG.Trace("Started MessageQueueTransaction for queue = [" + mq.Path + "]"); } #endregion @@ -457,7 +457,7 @@ namespace Spring.Messaging.Listener if (LOG.IsTraceEnabled) { - LOG.Trace("Receiving message with zero timeout for queue = [" + mq.QueueName + "]"); + LOG.Trace("Receiving message with zero timeout for queue = [" + mq.Path + "]"); } #endregion @@ -491,7 +491,7 @@ namespace Spring.Messaging.Listener if (LOG.IsErrorEnabled) { - LOG.Error("Error receiving message from DefaultMessageQueue [" + mq.QueueName + + LOG.Error("Error receiving message from DefaultMessageQueue [" + mq.Path + "], closing queue and clearing connection cache."); } @@ -514,7 +514,7 @@ namespace Spring.Messaging.Listener if (LOG.IsTraceEnabled) { - LOG.Trace("Message recieved is null from Queue = [" + mq.QueueName + "]"); + LOG.Trace("Message recieved is null from Queue = [" + mq.Path + "]"); } #endregion @@ -529,7 +529,7 @@ namespace Spring.Messaging.Listener if (LOG.IsDebugEnabled) { - LOG.Debug("Received message [" + message.Id + "] on queue [" + mq.QueueName + "]"); + LOG.Debug("Received message [" + message.Id + "] on queue [" + mq.Path + "]"); } #endregion @@ -560,7 +560,7 @@ namespace Spring.Messaging.Listener if (LOG.IsTraceEnabled) { - LOG.Trace("Committed MessageQueueTransaction for queue [" + mq.QueueName + "]"); + LOG.Trace("Committed MessageQueueTransaction for queue [" + mq.Path + "]"); } #endregion @@ -579,7 +579,7 @@ namespace Spring.Messaging.Listener { LOG.Debug( "Exception handler's TransactionAction has rolled back MessageQueueTransaction for queue [" + - mq.QueueName + "]"); + mq.Path + "]"); } #endregion @@ -595,7 +595,7 @@ namespace Spring.Messaging.Listener { LOG.Debug( "Exception handler's TransactionAction has committed MessageQueueTransaction for queue [" + - mq.QueueName + "]"); + mq.Path + "]"); } #endregion diff --git a/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueFactoryObject.cs b/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueFactoryObject.cs index 621a06c3..c8c45c59 100644 --- a/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueFactoryObject.cs +++ b/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueFactoryObject.cs @@ -20,6 +20,7 @@ using System; using System.Messaging; +using Spring.Objects.Factory; using Spring.Objects.Factory.Config; namespace Spring.Messaging.Support @@ -33,7 +34,7 @@ namespace Spring.Messaging.Support /// configuration of the MessageQueue. /// /// Mark Pollack - public class MessageQueueFactoryObject : IConfigurableFactoryObject + public class MessageQueueFactoryObject : IConfigurableFactoryObject, IObjectNameAware { // fields used in constructor private string path = string.Empty; @@ -52,6 +53,11 @@ namespace Spring.Messaging.Support private MessageQueueCreatorDelegate messageCreatorDelegate; + private bool remoteQueue = false; + + private bool remoteQueueIsTransactional = false; + private string objectName; + /// /// Gets or sets an instance of the MessageQueueCreator delegate that will be used to create the @@ -71,9 +77,9 @@ namespace Spring.Messaging.Support } /// - /// Gets or sets the path used to creat DefaultMessageQueue instance. + /// Gets or sets the path used to create a MessageQueue instance. /// - /// The location of the queue referenced by the DefaultMessageQueue. + /// The location of the queue. public string Path { get { return path; } @@ -82,7 +88,7 @@ namespace Spring.Messaging.Support /// - /// Gets or sets a value indicating whether to create the DefaultMessageQueue instance with + /// Gets or sets a value indicating whether to create the MessageQueue instance with /// exclusive read access to the first application that accesses the queue /// /// @@ -119,7 +125,7 @@ namespace Spring.Messaging.Support /// /// Sets a value indicating whether to enable connection cache. The default is false, which - /// is different than the default value when creating a DefaultMessageQueue object. + /// is different than the default value when creating a System.Messaging.MessageQueue object. /// /// /// true if enable connection cache; otherwise, false. @@ -154,6 +160,34 @@ namespace Spring.Messaging.Support set { messageReadPropertyFilterSetDefaults = value; } } + /// + /// Gets or sets a value indicating whether the queue is a remote queue. + /// + /// + /// The operations that one can perform on the MessageQueue depend on if it is local or remote, for + /// example checking if it is transactional. This is very difficult to determine programmatically. + /// The property was made virtual so it can be overridden to take into account custom heuristics you + /// may want to use to determine this programmatically. + /// + /// true if remote queue; otherwise, false. + public virtual bool RemoteQueue + { + get { return remoteQueue; } + set { remoteQueue = value; } + } + + /// + /// Gets or sets a value indicating whether the remote queue is transactional. + /// + /// + /// true if the remote queue is transactional; otherwise, false. + /// + public virtual bool RemoteQueueIsTransactional + { + get { return remoteQueueIsTransactional; } + set { remoteQueueIsTransactional = value; } + } + #region IFactoryObject Members /// @@ -188,7 +222,7 @@ namespace Spring.Messaging.Support /// creates, or /// if not known in advance. /// - /// The type DefaultMessageQueue + /// The type System.Messaging.MessageQueue public Type ObjectType { get { return typeof (MessageQueue); } @@ -219,5 +253,23 @@ namespace Spring.Messaging.Support #endregion #endregion + + /// + /// Set the name of the object in the object factory that created this object. + /// + /// The name of the object in the factory. + /// + ///

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

+ ///
+ public string ObjectName + { + set { objectName = value; } + get { return ObjectName; } + } } } \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Support/QueueUtils.cs b/src/Spring/Spring.Messaging/Messaging/Support/QueueUtils.cs index af619723..0b7470aa 100644 --- a/src/Spring/Spring.Messaging/Messaging/Support/QueueUtils.cs +++ b/src/Spring/Spring.Messaging/Messaging/Support/QueueUtils.cs @@ -19,6 +19,7 @@ #endregion using System; +using System.Collections; using System.Messaging; using Spring.Context; using Spring.Messaging.Core; @@ -78,6 +79,7 @@ namespace Spring.Messaging.Support return null; } } + } internal class MessageQueueResourceSynchronization : ITransactionSynchronization diff --git a/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj b/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj index 2596de2a..6d7ba218 100644 --- a/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj +++ b/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj @@ -50,6 +50,8 @@ CommonAssemblyInfo.cs + + diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueMetadataCacheTests.cs b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueMetadataCacheTests.cs new file mode 100644 index 00000000..9195d6a2 --- /dev/null +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueMetadataCacheTests.cs @@ -0,0 +1,70 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using NUnit.Framework; +using Spring.Testing.NUnit; + +#endregion + +namespace Spring.Messaging.Core +{ + /// + /// This class contains tests for MessageQueueTemplate + /// + /// Mark Pollack + [TestFixture] + public class MessageQueueMetadataCacheTests : AbstractDependencyInjectionSpringContextTests + { + protected override string[] ConfigLocations + { + get { return new[] {"assembly://Spring.Messaging.Tests/Spring.Messaging.Core/MessageQueueTemplateTests.xml"}; } + } + + [Test] + public void InitialzeMessageQueueMetadata() + { + var cache = new MessageQueueMetadataCache(applicationContext); + Assert.AreEqual(0, cache.Count); + cache.Initialize(); + Assert.IsTrue(cache.Initalized); + Assert.AreEqual(4, cache.Count); + MessageQueueMetadata md = cache.Get(@".\Private$\testqueue"); + cache.Remove(@".\Private$\testqueue"); + Assert.AreEqual(3, cache.Count); + Assert.IsNull(cache.Get(@".\Private$\testqueue")); + + var paths = new[] + { + @".\Private$\testtxqueue", + @"FormatName:Direct=TCP:192.168.1.105\Private$\testtxqueue", + @"FormatName:Direct=TCP:192.168.1.105\Private$\testqueue" + }; + + Assert.That(paths, Is.EquivalentTo(cache.Paths)); + paths = new[] {@".\Private$\testtxqueue", @"FormatName:Direct=TCP:192.168.1.105\Private$\testtxqueue"}; + cache.RemoveAll(paths); + Assert.AreEqual(1, cache.Count); + cache.Clear(); + Assert.AreEqual(0, cache.Count); + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueMetadataCacheTests.xml b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueMetadataCacheTests.xml new file mode 100644 index 00000000..da7def1c --- /dev/null +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueMetadataCacheTests.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.cs b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.cs index 9f011e54..7709d69c 100644 --- a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.cs +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.cs @@ -37,7 +37,7 @@ using Spring.Util; namespace Spring.Messaging.Core { /// - /// This class contains tests for + /// This class contains tests for MessageQueueTemplate /// /// Mark Pollack [TestFixture] @@ -102,6 +102,8 @@ namespace Spring.Messaging.Core mqt.MessageQueueFactory.RegisterMessageQueue("fooQueueDefinition", sc.CreateQueue ); } + + #endif public class SimpleCreator { @@ -139,16 +141,6 @@ namespace Spring.Messaging.Core Assert.AreEqual(q.DefaultMessageQueue, q.MessageQueueFactory.CreateMessageQueue(q.DefaultMessageQueueObjectName)); } - [Test, Ignore("obsolete test. If necessary, registers a default message converter on first use")] - [ExpectedException(typeof (InvalidOperationException), - ExpectedMessage = "No MessageConverter registered. Check configuration of MessageQueueTemplate.")] - public void MessageConverterNotRegistered() - { - MessageQueueTemplate q = applicationContext["queue-noconverter"] as MessageQueueTemplate; - Assert.IsNotNull(q); - IMessageConverter c = q.MessageConverter; - } - #region Integration Tests - to be moved to another test assembly [Test] @@ -160,6 +152,15 @@ namespace Spring.Messaging.Core ReceiveHelloWorld(null,q,1); } + [Test] + public void SendAndReceiveNonTransactionalRemotePrivateQueue() + { + MessageQueueTemplate q = applicationContext["queueTemplate-remote"] as MessageQueueTemplate; + Assert.IsNotNull(q); + q.ConvertAndSend("Hello World 1"); + //ReceiveHelloWorld(null, q, 1); + } + private static void ReceiveHelloWorld(string messageQueueObjectName, MessageQueueTemplate q, int index) { object o = null; @@ -201,6 +202,7 @@ namespace Spring.Messaging.Core Receive(null,q); } + private static void SendAndReceive(MessageQueueTemplate q) { SendAndReceive(null, q); @@ -255,7 +257,31 @@ namespace Spring.Messaging.Core #endregion - [Test, Ignore("What's the purpose of this test?")] + protected override string[] ConfigLocations + { + get { return new string[] { "assembly://Spring.Messaging.Tests/Spring.Messaging.Core/MessageQueueTemplateTests.xml" }; } + } + + + #region Some simple driver code for debugging + public void SimpleRemoteConsumption() + { + string connectionWorking = @"FormatName:Direct=OS:MARKT60\Private$\testqueue"; + + //TCP:IP doesn't work... + MessageQueue rmQ = new MessageQueue(@"FormatName:Direct=TCP:192.168.1.105\Private$\testqueue"); + + rmQ.Send("Hello Simple"); + + rmQ.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); + + Message msg = rmQ.Receive(); + + Assert.IsNotNull(msg); + + + } + public void GetAllFromQueue() { MessageQueueTemplate q = applicationContext["queue"] as MessageQueueTemplate; @@ -265,9 +291,7 @@ namespace Spring.Messaging.Core Console.WriteLine(q.ReceiveAndConvert()); } } - protected override string[] ConfigLocations - { - get { return new string[] {"assembly://Spring.Messaging.Tests/Spring.Messaging.Core/MessageQueueTemplateTests.xml"}; } - } + #endregion + } } \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.xml b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.xml index 242c5866..305a027a 100644 --- a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.xml +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.xml @@ -23,16 +23,18 @@ - - - - - - - - + + + + + + + + + + @@ -49,9 +51,32 @@ + + + + + + + + + + + + + - + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.cs b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.cs index a6b7c5ea..11269512 100644 --- a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.cs +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.cs @@ -84,15 +84,19 @@ namespace Spring.Messaging.Listener [Test] public void SendAndAsyncReceive() { - MessageQueueTemplate q = applicationContext["testQueueTemplate"] as MessageQueueTemplate; + //MessageQueueTemplate q = applicationContext["testQueueTemplate"] as MessageQueueTemplate; + MessageQueueTemplate q = applicationContext["testRemoteTemplate"] as MessageQueueTemplate; Assert.IsNotNull(q); + /* q.ConvertAndSend("Hello World 1"); q.ConvertAndSend("Hello World 2"); q.ConvertAndSend("Hello World 3"); q.ConvertAndSend("Hello World 4"); q.ConvertAndSend("Hello World 5"); + */ + Assert.AreEqual(0, listener.MessageCount); container.Start(); @@ -104,6 +108,7 @@ namespace Spring.Messaging.Listener container.Stop(); container.Shutdown(); Thread.Sleep(2500); + } protected override string[] ConfigLocations diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.xml b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.xml index 3f46da96..cbe1fc27 100644 --- a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.xml +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.xml @@ -33,8 +33,18 @@ + + + + + + + + + + - + @@ -44,7 +54,7 @@ - + diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/SimpleHandler.cs b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/SimpleHandler.cs index 22d58661..081a322c 100644 --- a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/SimpleHandler.cs +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/SimpleHandler.cs @@ -17,6 +17,10 @@ namespace Spring.Messaging.Listener private string stateVariable; + public SimpleHandler() + { + this.stateVariable = "hello"; + } public SimpleHandler(string stateVariable) { this.stateVariable = stateVariable; diff --git a/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2008.csproj b/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2008.csproj index 57a136d7..3bc405ce 100644 --- a/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2008.csproj +++ b/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2008.csproj @@ -80,11 +80,13 @@ + Always +