diff --git a/src/Spring/Spring.Core/Expressions/MethodNode.cs b/src/Spring/Spring.Core/Expressions/MethodNode.cs index f308ed42..cdf2460e 100644 --- a/src/Spring/Spring.Core/Expressions/MethodNode.cs +++ b/src/Spring/Spring.Core/Expressions/MethodNode.cs @@ -1,19 +1,19 @@ #region License -/* - * Copyright © 2002-2005 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. +/* + * Copyright © 2002-2005 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 @@ -95,6 +95,8 @@ namespace Spring.Expressions { string methodName = this.getText(); object[] argValues = ResolveArguments(evalContext); + ICollectionProcessor localCollectionProcessor = null; + IMethodCallProcessor methodCallProcessor = null; // resolve method, if necessary lock (this) @@ -102,35 +104,24 @@ namespace Spring.Expressions // check if it is a collection and the methodname denotes a collection processor if ((context == null || context is ICollection)) { - ICollectionProcessor localCollectionProcessor; // predefined collection processor? - localCollectionProcessor = (ICollectionProcessor) collectionProcessorMap[methodName]; + localCollectionProcessor = (ICollectionProcessor)collectionProcessorMap[methodName]; // user-defined collection processor? if (localCollectionProcessor == null && evalContext.Variables != null) { localCollectionProcessor = evalContext.Variables[methodName] as ICollectionProcessor; } - - if (localCollectionProcessor != null) - { - return localCollectionProcessor.Process((ICollection) context, argValues); - } } // try extension methods - IMethodCallProcessor methodCallProcessor = (IMethodCallProcessor)extensionMethodProcessorMap[methodName]; + methodCallProcessor = (IMethodCallProcessor)extensionMethodProcessorMap[methodName]; { // user-defined extension method processor? if (methodCallProcessor == null && evalContext.Variables != null) { methodCallProcessor = evalContext.Variables[methodName] as IMethodCallProcessor; } - - if (methodCallProcessor != null) - { - return methodCallProcessor.Process(context, argValues); - } } // try instance method @@ -148,18 +139,28 @@ namespace Spring.Expressions Initialize(methodName, argValues, context); initialized = true; } - - if (cachedInstanceMethod != null) - { - object[] paramValues = (cachedIsParamArray) - ? ReflectionUtils.PackageParamArray(argValues, argumentCount, paramArrayType) - : argValues; - return cachedInstanceMethod.Invoke(context, paramValues); - } } } - - throw new ArgumentException(string.Format("Method '{0}' with the specified number and types of arguments does not exist.", methodName)); + + if (localCollectionProcessor != null) + { + return localCollectionProcessor.Process((ICollection)context, argValues); + } + else if (methodCallProcessor != null) + { + return methodCallProcessor.Process(context, argValues); + } + else if (cachedInstanceMethod != null) + { + object[] paramValues = (cachedIsParamArray) + ? ReflectionUtils.PackageParamArray(argValues, argumentCount, paramArrayType) + : argValues; + return cachedInstanceMethod.Invoke(context, paramValues); + } + else + { + throw new ArgumentException(string.Format("Method '{0}' with the specified number and types of arguments does not exist.", methodName)); + } } private int CalculateMethodHash(Type contextType, object[] argValues) @@ -268,21 +269,21 @@ namespace Spring.Expressions } // used to calculate signature hash while caring for arg positions - private static readonly int[] s_primes = - { - 17, 19, 23, 29 - , 31, 37, 41, 43, 47, 53, 59, 61, 67, 71 - , 73, 79, 83, 89, 97, 101, 103, 107, 109, 113 - , 127, 131, 137, 139, 149, 151, 157, 163, 167, 173 - , 179, 181, 191, 193, 197, 199, 211, 223, 227, 229 - , 233, 239, 241, 251, 257, 263, 269, 271, 277, 281 - , 283, 293, 307, 311, 313, 317, 331, 337, 347, 349 - , 353, 359, 367, 373, 379, 383, 389, 397, 401, 409 - , 419, 421, 431, 433, 439, 443, 449, 457, 461, 463 - , 467, 479, 487, 491, 499, 503, 509, 521, 523, 541 - , 547, 557, 563, 569, 571, 577, 587, 593, 599, 601 - , 607, 613, 617, 619, 631, 641, 643, 647, 653, 659 - , 661, 673, 677, 683, 691, 701, 709, 719, 727, 733 + private static readonly int[] s_primes = + { + 17, 19, 23, 29 + , 31, 37, 41, 43, 47, 53, 59, 61, 67, 71 + , 73, 79, 83, 89, 97, 101, 103, 107, 109, 113 + , 127, 131, 137, 139, 149, 151, 157, 163, 167, 173 + , 179, 181, 191, 193, 197, 199, 211, 223, 227, 229 + , 233, 239, 241, 251, 257, 263, 269, 271, 277, 281 + , 283, 293, 307, 311, 313, 317, 331, 337, 347, 349 + , 353, 359, 367, 373, 379, 383, 389, 397, 401, 409 + , 419, 421, 431, 433, 439, 443, 449, 457, 461, 463 + , 467, 479, 487, 491, 499, 503, 509, 521, 523, 541 + , 547, 557, 563, 569, 571, 577, 587, 593, 599, 601 + , 607, 613, 617, 619, 631, 641, 643, 647, 653, 659 + , 661, 673, 677, 683, 691, 701, 709, 719, 727, 733 }; } } diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/DistributedTxMessageListenerContainerTests.cs b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/DistributedTxMessageListenerContainerTests.cs index ba93916c..1770980f 100644 --- a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/DistributedTxMessageListenerContainerTests.cs +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/DistributedTxMessageListenerContainerTests.cs @@ -81,11 +81,11 @@ namespace Spring.Messaging.Listener q.ConvertAndSend("Goodbye World 1"); Assert.AreEqual(0, listener.MessageCount, "PRECONDITION FAILURE: Unable to send the message!"); + distributedTxMessageListenerContainer.Start(); Thread.Sleep(waitInMillis); - distributedTxMessageListenerContainer.Stop(); distributedTxMessageListenerContainer.Shutdown(); Thread.Sleep(2500); @@ -105,6 +105,8 @@ namespace Spring.Messaging.Listener //must match the retry count in the object registration for test to pass! const int EXCEPTION_QUEUE_RETRY_COUNT = 2; + int expectedMessageCount = MESSAGE_COUNT + (MESSAGE_COUNT * EXCEPTION_QUEUE_RETRY_COUNT); + MessageQueueTemplate q = applicationContext["queueTemplate"] as MessageQueueTemplate; Assert.IsNotNull(q); @@ -115,13 +117,22 @@ namespace Spring.Messaging.Listener Assert.AreEqual(0, listener.MessageCount); + System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); + timer.Start(); + distributedTxMessageListenerContainer.Start(); - //this test needs to wait somewhat longer than the others in order to consistently pass so - //artificially inflate the waiting period before attempting subsequent asserts: - Thread.Sleep((int)(waitInMillis * 1.5)); + while (listener.MessageCount < expectedMessageCount) + { + if (timer.ElapsedMilliseconds > 60000) + Assert.Fail("Did not receive expected number of messages within the permitted time limit."); + } - Assert.AreEqual(MESSAGE_COUNT + (MESSAGE_COUNT * EXCEPTION_QUEUE_RETRY_COUNT), listener.MessageCount); + timer.Stop(); + + System.Diagnostics.Debug.WriteLine("elapsed time = " + timer.ElapsedMilliseconds); + + Assert.AreEqual(expectedMessageCount, listener.MessageCount); distributedTxMessageListenerContainer.Stop(); distributedTxMessageListenerContainer.Shutdown(); diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/MultiThreadedNonTransactionalMessageListenerContainerTests.cs b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/MultiThreadedNonTransactionalMessageListenerContainerTests.cs new file mode 100644 index 00000000..6c79e12c --- /dev/null +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/MultiThreadedNonTransactionalMessageListenerContainerTests.cs @@ -0,0 +1,120 @@ +#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 System.Threading; +using NUnit.Framework; +using Spring.Messaging.Core; +using Spring.Testing.NUnit; +using System.Diagnostics; +using System; + +#endregion + +namespace Spring.Messaging.Listener +{ + /// + /// This class contains tests for + /// + /// Mark Pollack + /// $Id:$ + [TestFixture] + public class MultiThreadedNonTransactionalMessageListenerContainerTests : AbstractDependencyInjectionSpringContextTests + { + + private NonTransactionalMessageListenerContainer container; + private WaitingHandler listener; + private SimpleExceptionHandler exceptionHandler; + + [SetUp] + public override void SetUp() + { + MessageQueueUtils.RecreateMessageQueue(@".\Private$\testqueue", false); + MessageQueueUtils.RecreateMessageQueue(@".\Private$\testresponsequeue", false); + base.SetUp(); + } + + public SimpleExceptionHandler ExceptionHandler + { + set { exceptionHandler = value; } + } + + public NonTransactionalMessageListenerContainer Container + { + get { return container; } + set { container = value; } + } + + public WaitingHandler Listener + { + get { return listener; } + set { listener = value; } + } + + + [Test] + public void Test() + { + System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); + + MessageQueueTemplate q = applicationContext["testQueueTemplate"] 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"); + + //Reset the state so that running all tests together will succeed. + exceptionHandler.MessageCount = 0; + + Assert.AreEqual(0, listener.MessageCount); + + timer.Start(); + + container.Start(); + + while (listener.MessageCount < 5) + { + //provide an exit if the test is completely over-length + if (timer.ElapsedMilliseconds > 120000) + Assert.Fail("Did not receive expected number of messages with the expected time-limit!"); + } + + timer.Stop(); + + container.Stop(); + container.Shutdown(); + + Debug.WriteLine(String.Format("Elapsed Milliseconds: {0}", timer.ElapsedMilliseconds)); + + Assert.Less(timer.ElapsedMilliseconds, 50000); + Assert.AreEqual(5, listener.MessageCount); + Assert.AreEqual(0, exceptionHandler.MessageCount); + } + + protected override string[] ConfigLocations + { + get { return new string[] { "assembly://Spring.Messaging.Tests/Spring.Messaging.Listener/MultiThreadedNonTransactionalMessageListenerContainerTests.xml" }; } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/MultiThreadedNonTransactionalMessageListenerContainerTests.xml b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/MultiThreadedNonTransactionalMessageListenerContainerTests.xml new file mode 100644 index 00000000..5845daa5 --- /dev/null +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/MultiThreadedNonTransactionalMessageListenerContainerTests.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/WaitingHandler.cs b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/WaitingHandler.cs new file mode 100644 index 00000000..8f1ea80c --- /dev/null +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/WaitingHandler.cs @@ -0,0 +1,49 @@ + + +using System; +using Common.Logging; +using System.Threading; + +namespace Spring.Messaging.Listener +{ + public class WaitingHandler + { + #region Logging + + private static readonly ILog LOG = LogManager.GetLogger(typeof(WaitingHandler)); + + #endregion + + private int messageCount; + + private string stateVariable; + + public WaitingHandler() + { + this.stateVariable = "hello"; + } + public WaitingHandler(string stateVariable) + { + this.stateVariable = stateVariable; + } + + + public int MessageCount + { + get { return messageCount; } + set { messageCount = value; } + } + + public string HandleMessage(string msgTxt) + { + LOG.Debug(String.Format("Received text = [{0}]", msgTxt)); + LOG.Debug("constructor set state string = " + stateVariable); + + Thread.Sleep(10000); + + messageCount++; + LOG.Debug("Message listener count = " + messageCount); + return msgTxt + " - processed!"; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2005.csproj b/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2005.csproj index 226e0bd1..8350424c 100644 --- a/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2005.csproj +++ b/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2005.csproj @@ -75,6 +75,7 @@ + @@ -87,11 +88,13 @@ + - + + Code 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 fc0bcbe0..f451fbca 100644 --- a/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2008.csproj +++ b/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2008.csproj @@ -76,6 +76,7 @@ + @@ -88,13 +89,15 @@ + - + + Code diff --git a/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2010.csproj b/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2010.csproj index e0e3d53e..7a17a1db 100644 --- a/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2010.csproj +++ b/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2010.csproj @@ -56,6 +56,8 @@ + + @@ -88,6 +90,9 @@ + + Designer + Always