diff --git a/src/Spring/Spring.Messaging/Messaging/Core/DefaultMessageQueueFactory.cs b/src/Spring/Spring.Messaging/Messaging/Core/DefaultMessageQueueFactory.cs index a6c7b43d..329ad34b 100644 --- a/src/Spring/Spring.Messaging/Messaging/Core/DefaultMessageQueueFactory.cs +++ b/src/Spring/Spring.Messaging/Messaging/Core/DefaultMessageQueueFactory.cs @@ -18,10 +18,11 @@ #endregion - +using System; using System.Collections; using System.Messaging; using Spring.Context; +using Spring.Messaging.Support; using Spring.Messaging.Support.Converters; using Spring.Threading; using Spring.Util; @@ -30,7 +31,8 @@ namespace Spring.Messaging.Core { /// /// A implementation that caches MessageQueue and IMessageConverter - /// instances. + /// instances. The MessageQueue objects are created by retrieving them by-name from the + /// ApplicationContext. /// /// Mark Pollack public class DefaultMessageQueueFactory : IMessageQueueFactory, IApplicationContextAware @@ -41,10 +43,18 @@ namespace Spring.Messaging.Core private static readonly string CONVERTER_DICTIONARY_SLOTNAME = UniqueKey.GetTypeScopedString(typeof (DefaultMessageQueueFactory), "Converter"); - private IApplicationContext applicationContext; + private IConfigurableApplicationContext applicationContext; #region IMessageQueueFactory Members + public void RegisterMessageQueue(string messageQueueObjectName, + MessageQueueCreatorDelegate messageQueueCreatorDelegate) + { + MessageQueueFactoryObject mqfo = new MessageQueueFactoryObject(); + mqfo.MessageCreatorDelegate = messageQueueCreatorDelegate; + applicationContext.ObjectFactory.RegisterSingleton(messageQueueObjectName, mqfo); + } + public MessageQueue CreateMessageQueue(string messageQueueObjectName) { AssertUtils.ArgumentHasText(messageQueueObjectName, "DefaultMessageQueueObjectName"); @@ -62,21 +72,40 @@ namespace Spring.Messaging.Core return queues[messageQueueObjectName] as MessageQueue; } - public IMessageConverter CreateMessageConverter(string messgaeConverterObjectName) + public bool ContainsMessageQueue(string messageQueueObjectName) { - AssertUtils.ArgumentHasText(messgaeConverterObjectName, "MessgaeFormatterObjectName"); + return applicationContext.ContainsObject(messageQueueObjectName); + } + + public void RegisterMessageConverter(string messageConverterName, + MessageConverterCreatorDelegate messageConverterCreatorDelegate) + { + MessageConverterFactoryObject mcfo = new MessageConverterFactoryObject(); + mcfo.MessageConverterCreatorDelegate = messageConverterCreatorDelegate; + applicationContext.ObjectFactory.RegisterSingleton(messageConverterName, mcfo); + } + + public IMessageConverter CreateMessageConverter(string messageConverterObjectName) + { + AssertUtils.ArgumentHasText(messageConverterObjectName, "MessgaeFormatterObjectName"); IDictionary converters = LogicalThreadContext.GetData(CONVERTER_DICTIONARY_SLOTNAME) as IDictionary; if (converters == null) { converters = new Hashtable(); LogicalThreadContext.SetData(CONVERTER_DICTIONARY_SLOTNAME, converters); } - if (!converters.Contains(messgaeConverterObjectName)) + if (!converters.Contains(messageConverterObjectName)) { - IMessageConverter mc = applicationContext.GetObject(messgaeConverterObjectName) as IMessageConverter; - converters.Add(messgaeConverterObjectName, mc); + IMessageConverter mc = applicationContext.GetObject(messageConverterObjectName) as IMessageConverter; + converters.Add(messageConverterObjectName, mc); } - return converters[messgaeConverterObjectName] as IMessageConverter; + return converters[messageConverterObjectName] as IMessageConverter; + } + + + public bool ContainsMessageConverter(string messageConverterObjectName) + { + return applicationContext.ContainsObject(messageConverterObjectName); } #endregion @@ -112,7 +141,18 @@ namespace Spring.Messaging.Core public IApplicationContext ApplicationContext { get { return applicationContext; } - set { applicationContext = value; } + set + { + AssertUtils.ArgumentNotNull(value, "An ApplicationContext instance is required"); + IConfigurableApplicationContext ctx = value as IConfigurableApplicationContext; + if (ctx == null) + { + throw new InvalidOperationException( + "Implementations of IApplicationContext must also implement IConfigurableApplicationContext"); + } + + applicationContext = ctx; + } } #endregion diff --git a/src/Spring/Spring.Messaging/Messaging/Core/IMessageQueueFactory.cs b/src/Spring/Spring.Messaging/Messaging/Core/IMessageQueueFactory.cs index 1326beba..6292b81b 100644 --- a/src/Spring/Spring.Messaging/Messaging/Core/IMessageQueueFactory.cs +++ b/src/Spring/Spring.Messaging/Messaging/Core/IMessageQueueFactory.cs @@ -19,23 +19,42 @@ #endregion using System.Messaging; +using Spring.Messaging.Support; using Spring.Messaging.Support.Converters; namespace Spring.Messaging.Core { /// - /// An interface for creating MessageQueue and IMessageConverter objects. + /// An interface for creating MessageQueue and IMessageConverter objects from object definitions + /// defined in the application context. /// /// - /// These objects have methods that are generally not thread safe, (IMessageConverter classes - /// rely on IMessageFormatter objects that are not thread safe). A major reason to - /// for this interface is to provide thread-local instances such that appliation code need - /// not be concerned with these resource management issues. + /// MessageQueue and IMessageConverter objects have methods that are generally not thread safe, + /// (IMessageConverter classes rely on IMessageFormatter objects that are not thread safe). + /// As such, a major reason to for this interface is to provide thread-local instances such that + /// appliation code need not be concerned with these resource management issues. /// + /// Mark Pollack public interface IMessageQueueFactory { + void RegisterMessageQueue(string messageQueueObjectName, + MessageQueueCreatorDelegate messageQueueCreatorDelegate); + MessageQueue CreateMessageQueue(string messageQueueObjectName); + bool ContainsMessageQueue(string messageQueueObjectName); + + + + void RegisterMessageConverter(string messageConverterName, + MessageConverterCreatorDelegate MessageConverterCreatorDelegate); + + IMessageConverter CreateMessageConverter(string messageConverterObjectName); + + bool ContainsMessageConverter(string messageConverterObjectName); + + + } } \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Core/IMessageQueueOperations.cs b/src/Spring/Spring.Messaging/Messaging/Core/IMessageQueueOperations.cs index b28af25b..cee8b6ac 100644 --- a/src/Spring/Spring.Messaging/Messaging/Core/IMessageQueueOperations.cs +++ b/src/Spring/Spring.Messaging/Messaging/Core/IMessageQueueOperations.cs @@ -88,6 +88,11 @@ namespace Spring.Messaging.Core /// if thrown by MSMQ API methods void ConvertAndSend(string messageQueueObjectName, object obj, MessagePostProcessorDelegate messagePostProcessorDelegate); + /* + void ConvertAndSend(QueueIdentifierType queueIdentifierType, string destinationValue, string messageQueueObjectName, object obj); + + void ConvertAndSend(QueueIdentifierType queueIdentifierType, string destinationValue, string messageQueueObjectName, object obj, MessagePostProcessorDelegate messagePostProcessorDelegate); + */ /// /// Receive and convert a message synchronously from the default message queue. /// diff --git a/src/Spring/Spring.Messaging/Messaging/Core/QueueIdentifierType.cs b/src/Spring/Spring.Messaging/Messaging/Core/QueueIdentifierType.cs new file mode 100644 index 00000000..11f41a81 --- /dev/null +++ b/src/Spring/Spring.Messaging/Messaging/Core/QueueIdentifierType.cs @@ -0,0 +1,23 @@ + + +namespace Spring.Messaging.Core +{ + public enum QueueIdentifierType + { + /// + /// Use a Label to identify the queue, for example new MessageQueue("Label:TheLabel"); + /// + Label, + + /// + /// Use a FormatName to idenitfy the queue, + /// + FormatName, + + MachineName, + + Path, + + QueueName, + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Support/Converters/MessageConverterCreatorDelegate.cs b/src/Spring/Spring.Messaging/Messaging/Support/Converters/MessageConverterCreatorDelegate.cs new file mode 100644 index 00000000..bb9156e0 --- /dev/null +++ b/src/Spring/Spring.Messaging/Messaging/Support/Converters/MessageConverterCreatorDelegate.cs @@ -0,0 +1,31 @@ +#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 Spring.Messaging.Core; +using Spring.Messaging.Support.Converters; + +namespace Spring.Messaging.Support.Converters +{ + /// + /// Delegate for creating IMessageConverter instance. Used by + /// to register a creation function with a given name. + /// + public delegate IMessageConverter MessageConverterCreatorDelegate(); +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Support/Converters/MessageConverterFactoryObject.cs b/src/Spring/Spring.Messaging/Messaging/Support/Converters/MessageConverterFactoryObject.cs new file mode 100644 index 00000000..952afd8e --- /dev/null +++ b/src/Spring/Spring.Messaging/Messaging/Support/Converters/MessageConverterFactoryObject.cs @@ -0,0 +1,80 @@ +#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; +using Spring.Objects.Factory.Config; + +namespace Spring.Messaging.Support.Converters +{ + /// + /// Internal class to that users can specify a delegate function to register with the application context that + /// will create a IMessageConverter instance easily at runtime. + /// + /// Mark Pollack + internal class MessageConverterFactoryObject : IConfigurableFactoryObject + { + private IObjectDefinition productTemplate; + + private MessageConverterCreatorDelegate messageConverterCreatorDelegate; + + + public MessageConverterCreatorDelegate MessageConverterCreatorDelegate + { + get { return messageConverterCreatorDelegate; } + set { messageConverterCreatorDelegate = value; } + } + + #region IConfigurableFactoryObject Members + + + /// + /// Gets the template object definition that should be used + /// to configure the instance of the object managed by this factory. + /// + /// The object definition to configure the factory's product + public IObjectDefinition ProductTemplate + { + get { return productTemplate; } + set { productTemplate = value; } + } + + #endregion + + #region IFactoryObject Members + + public object GetObject() + { + return MessageConverterCreatorDelegate(); + } + + public Type ObjectType + { + get { return typeof(IMessageConverter); } + } + + public bool IsSingleton + { + get { return false; } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueCreatorDelegate.cs b/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueCreatorDelegate.cs new file mode 100644 index 00000000..2c530c5a --- /dev/null +++ b/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueCreatorDelegate.cs @@ -0,0 +1,13 @@ + + +using System.Messaging; +using Spring.Messaging.Core; + +namespace Spring.Messaging.Support +{ + /// + /// Delegate for creating MessageQueue instance. Used by + /// to register a creation function with a given name. + /// + public delegate MessageQueue MessageQueueCreatorDelegate(); +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueFactoryObject.cs b/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueFactoryObject.cs index 10e7c149..209cc937 100644 --- a/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueFactoryObject.cs +++ b/src/Spring/Spring.Messaging/Messaging/Support/MessageQueueFactoryObject.cs @@ -25,8 +25,13 @@ using Spring.Objects.Factory.Config; namespace Spring.Messaging.Support { /// - /// Factory for creating MessageQueues + /// Factory for creating MessageQueues. This factory will create prototype instances, i.e. every call to GetObject + /// will return a new MessageQueue object. /// + /// All MessageQueue constructor arguments are exposed as properties of the factory object. As this + /// is a use the PropertyTemplate property to specify additional + /// configuration of the MessageQueue. + /// /// Mark Pollack public class MessageQueueFactoryObject : IConfigurableFactoryObject { @@ -45,7 +50,25 @@ namespace Spring.Messaging.Support private bool messageReadPropertyFilterSetDefaults = false; - //myQueue.MessageReadPropertyFilter.SetAll(); + private MessageQueueCreatorDelegate messageCreatorDelegate; + + + /// + /// Gets or sets an instance of the MessageQueueCreator delegate that will be used to create the + /// MessageQueue object, instead of using the various public properties on this class such + /// as Path, AccessMode, etc. Not intended for end-users but rather as a means to help + /// register MessageQueueFactoryObject at runtime using convenience method on the IMessageQueueFactory. + /// + /// + /// Can also be specifed using an instance of MessageCreatorDelegate. If both are specifed, the + /// Interface implementation has priority. + /// + /// The function that is responsbile for creating a message queue. + public MessageQueueCreatorDelegate MessageCreatorDelegate + { + get { return messageCreatorDelegate; } + set { messageCreatorDelegate = value; } + } /// /// Gets or sets the path used to creat DefaultMessageQueue instance. @@ -139,17 +162,24 @@ namespace Spring.Messaging.Support /// A newly configured MessageQueue object public object GetObject() { - MessageQueue.EnableConnectionCache = enableConnectionCache; - MessageQueue mq = new MessageQueue(Path, DenySharedReceive, EnableCache, AccessMode); - if (messageReadPropertyFilterSetDefaults) + if (MessageCreatorDelegate != null) { - mq.MessageReadPropertyFilter.SetDefaults(); + return MessageCreatorDelegate(); } - if (messageReadPropertyFilterSetAll) + else { - mq.MessageReadPropertyFilter.SetAll(); + MessageQueue.EnableConnectionCache = enableConnectionCache; + MessageQueue mq = new MessageQueue(Path, DenySharedReceive, EnableCache, AccessMode); + if (messageReadPropertyFilterSetDefaults) + { + mq.MessageReadPropertyFilter.SetDefaults(); + } + if (messageReadPropertyFilterSetAll) + { + mq.MessageReadPropertyFilter.SetAll(); + } + return mq; } - return mq; } /// diff --git a/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj b/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj index 9e7833fa..4b774d0c 100644 --- a/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj +++ b/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj @@ -49,7 +49,10 @@ CommonAssemblyInfo.cs + + + @@ -78,6 +81,7 @@ + diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.cs b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.cs index 07352779..2be48805 100644 --- a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.cs +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.cs @@ -21,6 +21,7 @@ #region Imports using System; +using System.Messaging; using System.Transactions; using NUnit.Framework; using Spring.Data.Core; @@ -43,7 +44,46 @@ namespace Spring.Messaging.Core [TestFixture] public class MessageQueueTemplateTests : AbstractDependencyInjectionSpringContextTests { - [Test] + + [Test] + public void MessageCreator() + { + MessageQueueTemplate mqt = applicationContext["txqueue"] as MessageQueueTemplate; + Assert.IsNotNull(mqt); + string path = @".\Private$\mlptestqueue"; + if (MessageQueue.Exists(path)) + { + MessageQueue.Delete(path); + } + MessageQueue.Create(path, true); + mqt.MessageQueueFactory.RegisterMessageQueue("newQueueDefinition", delegate + { + MessageQueue mq = new MessageQueue(); + mq.Path = path; + // other properties + return mq; + }); + + Assert.IsTrue(mqt.MessageQueueFactory.ContainsMessageQueue("newQueueDefinition")); + + SendAndReceive("newQueueDefinition",mqt); + + SimpleCreator sc = new SimpleCreator(); + mqt.MessageQueueFactory.RegisterMessageQueue("fooQueueDefinition", sc.CreateQueue ); + + } + + public class SimpleCreator + { + public MessageQueue CreateQueue() + { + return new MessageQueue(); + } + } + + + + [Test] [ExpectedException(typeof (ArgumentException), ExpectedMessage = "DefaultMessageQueueObjectName is required.")] public void NoMessageQueueNameSpecified() { @@ -86,12 +126,19 @@ namespace Spring.Messaging.Core { MessageQueueTemplate q = applicationContext["queue"] as MessageQueueTemplate; Assert.IsNotNull(q); - ReceiveHelloWorld(q,1); + ReceiveHelloWorld(null,q,1); } - private static void ReceiveHelloWorld(MessageQueueTemplate q, int index) + private static void ReceiveHelloWorld(string messageQueueObjectName, MessageQueueTemplate q, int index) { - object o = q.ReceiveAndConvert(); + object o = null; + if (messageQueueObjectName == null) + { + o = q.ReceiveAndConvert(); + } else + { + o = q.ReceiveAndConvert(messageQueueObjectName); + } Assert.IsNotNull(o); string data = o as string; Assert.IsNotNull(data); @@ -103,7 +150,7 @@ namespace Spring.Messaging.Core { MessageQueueTemplate q = applicationContext["queue"] as MessageQueueTemplate; Assert.IsNotNull(q); - SendAndRecieve(q); + SendAndReceive(q); } [Test] @@ -111,7 +158,7 @@ namespace Spring.Messaging.Core { MessageQueueTemplate q = applicationContext["txqueue"] as MessageQueueTemplate; Assert.IsNotNull(q); - SendAndRecieve(q); + SendAndReceive(q); } [Test] @@ -120,31 +167,44 @@ namespace Spring.Messaging.Core MessageQueueTemplate q = applicationContext["txqueue"] as MessageQueueTemplate; Assert.IsNotNull(q); SendUsingMessageTxScope(q); - Receive(q); + Receive(null,q); } - private static void SendAndRecieve(MessageQueueTemplate q) + private static void SendAndReceive(MessageQueueTemplate q) { - SendUsingMessageTx(q); - Receive(q); + SendAndReceive(null, q); } - private static void Receive(MessageQueueTemplate q) + private static void SendAndReceive(string messageQueueObjectName, MessageQueueTemplate q) { - ReceiveHelloWorld(q,1); - ReceiveHelloWorld(q,2); - ReceiveHelloWorld(q,3); + SendUsingMessageTx(messageQueueObjectName, q); + Receive(messageQueueObjectName, q); } - private static void SendUsingMessageTx(MessageQueueTemplate q) + private static void Receive(string messageQueueObjectName, MessageQueueTemplate q) + { + ReceiveHelloWorld(messageQueueObjectName, q, 1); + ReceiveHelloWorld(messageQueueObjectName, q, 2); + ReceiveHelloWorld(messageQueueObjectName, q, 3); + } + + private static void SendUsingMessageTx(string messageQueueObjectName, MessageQueueTemplate q) { IPlatformTransactionManager txManager = new MessageQueueTransactionManager(); TransactionTemplate transactionTemplate = new TransactionTemplate(txManager); transactionTemplate.Execute(delegate(ITransactionStatus status) { - q.ConvertAndSend("Hello World 1"); - q.ConvertAndSend("Hello World 2"); - q.ConvertAndSend("Hello World 3"); + if (messageQueueObjectName == null) + { + q.ConvertAndSend("Hello World 1"); + q.ConvertAndSend("Hello World 2"); + q.ConvertAndSend("Hello World 3"); + } else + { + q.ConvertAndSend(messageQueueObjectName, "Hello World 1"); + q.ConvertAndSend(messageQueueObjectName, "Hello World 2"); + q.ConvertAndSend(messageQueueObjectName, "Hello World 3"); + } return null; }); } diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.xml b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.xml index a69cc396..242c5866 100644 --- a/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.xml +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Core/MessageQueueTemplateTests.xml @@ -34,18 +34,18 @@ - + - + - +