SPRNET-1233 - Support for sending to remote private queue. Clean up debug logging that would cause issues with remote private queues.

This commit is contained in:
markpollack
2009-07-24 21:34:33 +00:00
parent 9635c82336
commit c217365748
22 changed files with 675 additions and 96 deletions

View File

@@ -1,7 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>9.0.30729</ProductVersion>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{710961A3-0DF4-49E4-A26E-F5B9C044AC84}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -189,9 +189,7 @@
<Compile Include="Context\IApplicationEventPublisher.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Context\IConfigurableApplicationContext.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Context\IConfigurableApplicationContext.cs" />
<Compile Include="Context\IHierarchicalMessageSource.cs">
<SubType>Code</SubType>
</Compile>

View File

@@ -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
{
/// <summary>
/// Encapsulates additional metadata information about the MessageQueue that can not be easily obtained
/// from the MessageQueue itself.
/// </summary>
public class MessageQueueMetadata
{
private bool remoteQueue;
private bool remoteQueueIsTransactional;
/// <summary>
/// Initializes a new instance of the <see cref="MessageQueueMetadata"/> class.
/// </summary>
/// <param name="remoteQueue">if set to <c>true</c> [remote queue].</param>
/// <param name="remoteQueueIsTransactional">if set to <c>true</c> [remote queue is transactional].</param>
public MessageQueueMetadata(bool remoteQueue, bool remoteQueueIsTransactional)
{
this.remoteQueue = remoteQueue;
this.remoteQueueIsTransactional = remoteQueueIsTransactional;
}
/// <summary>
/// Gets or sets a value indicating whether the queue is a remote queue.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <value><c>true</c> if remote queue; otherwise, <c>false</c>.</value>
public virtual bool RemoteQueue
{
get { return remoteQueue; }
}
/// <summary>
/// Gets or sets a value indicating whether the remote queue is transactional.
/// </summary>
/// <value>
/// <c>true</c> if the remote queue is transactional; otherwise, <c>false</c>.
/// </value>
public virtual bool RemoteQueueIsTransactional
{
get { return remoteQueueIsTransactional; }
}
}
}

View File

@@ -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;
}
/// <summary>
/// Gets the number of items in the cache.
/// </summary>
public int Count
{
get
{
lock (itemStore.SyncRoot)
{
return itemStore.Count;
}
}
}
public bool Initalized
{
get
{
lock (itemStore.SyncRoot)
{
return isInitialized;
}
}
}
/// <summary>
/// Gets a collection of all cache queue paths.
/// </summary>
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;
}
}
}
/// <summary>
/// Retrieves MessageQueueMetadata from the cache.
/// </summary>
/// <param name="queuePath">The queue path.</param>
/// <returns>
/// Item for the specified <paramref name="queuePath"/>, or <c>null</c>.
/// </returns>
public MessageQueueMetadata Get(string queuePath)
{
lock (itemStore.SyncRoot)
{
return (MessageQueueMetadata) itemStore[queuePath];
}
}
/// <summary>
/// Removes the specified queue path from the cache
/// </summary>
/// <param name="queuePath">The queue path.</param>
public void Remove(string queuePath)
{
lock (itemStore.SyncRoot)
{
itemStore.Remove(queuePath);
}
}
/// <summary>
/// Removes collection of MessageQueueMetaCache from the cache.
/// </summary>
/// <param name="queuePaths">
/// Array of MessageQueue paths to remove.
/// </param>
public void RemoveAll(string[] queuePaths)
{
lock (itemStore.SyncRoot)
{
foreach (string queuePath in queuePaths)
{
itemStore.Remove(queuePath);
}
}
}
/// <summary>
/// Removes all MessageQueueMetadata from the cache.
/// </summary>
public void Clear()
{
lock (itemStore.SyncRoot)
{
itemStore.Clear();
}
}
public void Insert(string path, MessageQueueMetadata messageQueueMetadata)
{
lock (itemStore.SyncRoot)
{
itemStore[path] = messageQueueMetadata;
}
}
}
}

View File

@@ -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; }
}
/// <summary>
/// Gets or sets the metadata cache.
/// </summary>
/// <value>The metadata cache.</value>
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.
/// </summary>
/// <remarks>If System.Transactions.Transaction.Current is null, then send based on
/// the transaction semantics of the queue definition. See <see cref="DoSendMessageQueueTransactional"/> </remarks>
/// the transaction semantics of the queue definition. See <see cref="DoSendMessageQueue"/> </remarks>
/// <param name="messageQueue">The message queue.</param>
/// <param name="message">The message.</param>
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
/// </remarks>
/// <param name="mq">The mq.</param>
/// <param name="msg">The MSG.</param>
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);
}
}
/// <summary>
/// Does the send message transaction.
/// </summary>
/// <param name="mq">The mq.</param>
/// <param name="transactionToUse">The transaction to use.</param>
/// <param name="msg">The MSG.</param>
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);
}
}
/// <summary>
/// Does the send message queue non transactional.
/// </summary>
/// <param name="mq">The mq.</param>
/// <param name="transactionToUse">The transaction to use.</param>
/// <param name="msg">The MSG.</param>
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);
}
}

View File

@@ -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
{
}
/// <summary>
/// Template method that gets called right before a new message is received, i.e.
/// messageQueue.Receive().
/// </summary>
/// <remarks>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.</remarks>
/// <param name="messageQueue"></param>
protected virtual void BeforeMessageReceived(MessageQueue messageQueue)
{
}
#endregion
}
}

View File

@@ -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

View File

@@ -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());

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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.
/// </remarks>
/// <author>Mark Pollack</author>
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;
/// <summary>
/// Gets or sets an instance of the MessageQueueCreator delegate that will be used to create the
@@ -71,9 +77,9 @@ namespace Spring.Messaging.Support
}
/// <summary>
/// Gets or sets the path used to creat DefaultMessageQueue instance.
/// Gets or sets the path used to create a MessageQueue instance.
/// </summary>
/// <value>The location of the queue referenced by the DefaultMessageQueue.</value>
/// <value>The location of the queue.</value>
public string Path
{
get { return path; }
@@ -82,7 +88,7 @@ namespace Spring.Messaging.Support
/// <summary>
/// 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
/// </summary>
/// <value>
@@ -119,7 +125,7 @@ namespace Spring.Messaging.Support
/// <summary>
/// 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.
/// </summary>
/// <value>
/// <c>true</c> if enable connection cache; otherwise, <c>false</c>.
@@ -154,6 +160,34 @@ namespace Spring.Messaging.Support
set { messageReadPropertyFilterSetDefaults = value; }
}
/// <summary>
/// Gets or sets a value indicating whether the queue is a remote queue.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <value><c>true</c> if remote queue; otherwise, <c>false</c>.</value>
public virtual bool RemoteQueue
{
get { return remoteQueue; }
set { remoteQueue = value; }
}
/// <summary>
/// Gets or sets a value indicating whether the remote queue is transactional.
/// </summary>
/// <value>
/// <c>true</c> if the remote queue is transactional; otherwise, <c>false</c>.
/// </value>
public virtual bool RemoteQueueIsTransactional
{
get { return remoteQueueIsTransactional; }
set { remoteQueueIsTransactional = value; }
}
#region IFactoryObject Members
/// <summary>
@@ -188,7 +222,7 @@ namespace Spring.Messaging.Support
/// <see cref="Spring.Objects.Factory.IFactoryObject"/> creates, or
/// <see langword="null"/> if not known in advance.
/// </summary>
/// <value>The type DefaultMessageQueue</value>
/// <value>The type System.Messaging.MessageQueue</value>
public Type ObjectType
{
get { return typeof (MessageQueue); }
@@ -219,5 +253,23 @@ namespace Spring.Messaging.Support
#endregion
#endregion
/// <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; }
get { return ObjectName; }
}
}
}

View File

@@ -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

View File

@@ -50,6 +50,8 @@
<Link>CommonAssemblyInfo.cs</Link>
</Compile>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Messaging\Core\MessageQueueMetadata.cs" />
<Compile Include="Messaging\Core\MessageQueueMetadataCache.cs" />
<Compile Include="Messaging\Support\Converters\MessageConverterCreatorDelegate.cs" />
<Compile Include="Messaging\Core\DefaultMessageQueueFactory.cs" />
<Compile Include="Messaging\Support\MessageQueueCreatorDelegate.cs" />

View File

@@ -0,0 +1,70 @@
#region License
/*
* Copyright <20> 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
using NUnit.Framework;
using Spring.Testing.NUnit;
#endregion
namespace Spring.Messaging.Core
{
/// <summary>
/// This class contains tests for MessageQueueTemplate
/// </summary>
/// <author>Mark Pollack</author>
[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);
}
}
}

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
<object id='testqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='.\Private$\testqueue'/>
<property name='DenySharedReceive' value='true'/>
<property name='AccessMode' value='Receive'/>
<property name='EnableCache' value='true'/>
<property name='ProductTemplate'>
<object>
<property name='Label' value='MyLabel'/>
</object>
</property>
</object>
<object id='testtxqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='.\Private$\testtxqueue'/>
<property name='ProductTemplate'>
<object>
<property name='Label' value='MyTxLabel'/>
</object>
</property>
</object>
<object id='testremotetxqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='FormatName:Direct=TCP:192.168.1.105\Private$\testtxqueue'/>
<property name='RemoteQueue' value="true"/>
<property name="RemoteQueueIsTransactional" value="true"/>
</object>
<object id='testremotequeue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='FormatName:Direct=TCP:192.168.1.105\Private$\testqueue'/>
<property name='RemoteQueue' value="true"/>
</object>
<object id='msqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='.\Private$\testqueue'/>
<property name='ProductTemplate'>
<object>
<property name='Label' value='MyLabel'/>
</object>
</property>
</object>
</objects>

View File

@@ -37,7 +37,7 @@ using Spring.Util;
namespace Spring.Messaging.Core
{
/// <summary>
/// This class contains tests for
/// This class contains tests for MessageQueueTemplate
/// </summary>
/// <author>Mark Pollack</author>
[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
}
}

View File

@@ -23,16 +23,18 @@
</property>
</object>
<object id='msqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='.\Private$\testqueue'/>
<property name='ProductTemplate'>
<object>
<property name='Label' value='MyLabel'/>
</object>
</property>
<object id='testremotetxqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='FormatName:Direct=OS:MARKT60\Private$\testqueue'/>
<property name='RemoteQueue' value="true"/>
<property name="RemoteQueueIsTransactional" value="true"/>
</object>
<object id='testremotequeue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='FormatName:Direct=OS:MARKT60\Private$\testqueue'/>
<property name='RemoteQueue' value="true"/>
</object>
<object id="queue" type="Spring.Messaging.Core.MessageQueueTemplate, Spring.Messaging">
<property name="DefaultMessageQueueObjectName" value="testtxqueue"/>
<property name="MessageConverterObjectName" value="messageConverter"/>
@@ -49,9 +51,32 @@
<property name="MessageConverterObjectName" value="messageConverter"/>
</object>
<object id="queueTemplate-remote" type="Spring.Messaging.Core.MessageQueueTemplate, Spring.Messaging">
<property name="DefaultMessageQueueObjectName" value="testremotequeue"/>
<property name="MessageConverterObjectName" value="messageConverter"/>
</object>
<object id="queueTemplate-remote-tx" type="Spring.Messaging.Core.MessageQueueTemplate, Spring.Messaging">
<property name="DefaultMessageQueueObjectName" value="testremotetxqueue"/>
<property name="MessageConverterObjectName" value="messageConverter"/>
</object>
<object id="messageConverter" type="Spring.Messaging.Support.Converters.XmlMessageConverter, Spring.Messaging"
singleton="false">
<property name="TargetTypes" value="System.String"/>
</object>
<object id='msqueue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='.\Private$\testqueue'/>
<property name='ProductTemplate'>
<object>
<property name='Label' value='MyLabel'/>
</object>
</property>
</object>
</objects>

View File

@@ -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

View File

@@ -33,8 +33,18 @@
</property>
</object>
<object id='testremotequeue' type='Spring.Messaging.Support.MessageQueueFactoryObject, Spring.Messaging'>
<property name='Path' value='FormatName:Direct=OS:MARKT60\Private$\testqueue'/>
<property name='RemoteQueue' value="true"/>
</object>
<object id="testRemoteTemplate" type="Spring.Messaging.Core.MessageQueueTemplate, Spring.Messaging">
<property name="DefaultMessageQueueObjectName" value="testremotequeue"/>
<property name="MessageConverterObjectName" value="messageConverter"/>
</object>
<object id="testQueueTemplate" type="Spring.Messaging.Core.MessageQueueTemplate, Spring.Messaging">
<property name="MessageQueueObjectName" value="msmqTestQueue"/>
<property name="DefaultMessageQueueObjectName" value="msmqTestQueue"/>
<property name="MessageConverterObjectName" value="messageConverter"/>
</object>
@@ -44,7 +54,7 @@
</object>
<object id="nonTransactionalMessageListenerContainer" type="Spring.Messaging.Listener.NonTransactionalMessageListenerContainer, Spring.Messaging">
<property name="MessageQueueObjectName" value="msmqTestQueue"/>
<property name="MessageQueueObjectName" value="testremotequeue"/>
<property name="MaxConcurrentListeners" value="2"/>
<property name="ListenerTimeLimit" value="20s"/>
<property name="MessageListener" ref="messageListenerAdapter"/>

View File

@@ -17,6 +17,10 @@ namespace Spring.Messaging.Listener
private string stateVariable;
public SimpleHandler()
{
this.stateVariable = "hello";
}
public SimpleHandler(string stateVariable)
{
this.stateVariable = stateVariable;

View File

@@ -80,11 +80,13 @@
<EmbeddedResource Include="Messaging\queue-context.xml" />
<EmbeddedResource Include="Messaging\Core\MessageQueueTemplateTests.xml" />
<EmbeddedResource Include="Messaging\Listener\DistributedTxMessageListenerContainerTests.xml" />
<EmbeddedResource Include="Messaging\Core\MessageQueueMetadataCacheTests.xml" />
<Content Include="Spring.Messaging.Tests.dll.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Include="Messaging\Core\MessageQueueMetadataCacheTests.cs" />
<Compile Include="Messaging\Core\ThreadingTests.cs" />
<Compile Include="Messaging\Listener\DistributedTxMessageListenerContainerTests.cs" />
<Compile Include="Messaging\Listener\LoggingExceptionHandler.cs" />