diff --git a/src/Spring/Spring.Messaging/Messaging/Listener/ListenerExecutionFailedException.cs b/src/Spring/Spring.Messaging/Messaging/Listener/ListenerExecutionFailedException.cs new file mode 100644 index 00000000..7cd40973 --- /dev/null +++ b/src/Spring/Spring.Messaging/Messaging/Listener/ListenerExecutionFailedException.cs @@ -0,0 +1,52 @@ +#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; + +namespace Spring.Messaging.Listener +{ + /// + /// Exception to be thrown when the execution of a listener method failed. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class ListenerExecutionFailedException : MessagingException + { + + /// + /// Initializes a new instance of the class, with the specified message + /// + /// The message. + public ListenerExecutionFailedException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class, with the specified message + /// and root cause exception + /// + /// The message. + /// The inner exception. + public ListenerExecutionFailedException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging/Messaging/Listener/MessageListenerAdapter.cs b/src/Spring/Spring.Messaging/Messaging/Listener/MessageListenerAdapter.cs index 684a8fdc..bd319a10 100644 --- a/src/Spring/Spring.Messaging/Messaging/Listener/MessageListenerAdapter.cs +++ b/src/Spring/Spring.Messaging/Messaging/Listener/MessageListenerAdapter.cs @@ -18,6 +18,7 @@ #endregion +using System; using System.Collections; using System.Messaging; using Common.Logging; @@ -191,10 +192,20 @@ namespace Spring.Messaging.Listener get { return defaultHandlerMethod; } set { defaultHandlerMethod = value; - processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)"); + ProcessingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)"); } } + /// + /// Gets or sets the processing expression for use in custom subclasses + /// + /// The processing expression. + protected IExpression ProcessingExpression + { + get { return processingExpression; } + set { processingExpression = value; } + } + #region IInitializingObject Members /// @@ -384,11 +395,16 @@ namespace Spring.Messaging.Listener { object convertedMessage = ExtractMessage(message); - IDictionary vars = new Hashtable(); - vars["convertedObject"] = convertedMessage; + //IDictionary vars = new Hashtable(); + //vars["convertedObject"] = convertedMessage; + string methodName = GetHandlerMethodName(message, convertedMessage); + object[] listenerArguments = BuildListenerArguments(convertedMessage); + object result = InvokeListenerMethod(methodName, listenerArguments); + + //Invoke message handler method and get result. - object result = processingExpression.GetValue(handlerObject, vars); + //object result = processingExpression.GetValue(handlerObject, vars); if (result != null) { HandleResult(result, message); @@ -412,12 +428,55 @@ namespace Spring.Messaging.Listener return DefaultHandlerMethod; } + /// + /// Builds an array of arguments to be passed into the taret listener method. + /// + /// + /// Allows for multiple method arguments to be built from a single message object. + ///

The default implementation builds an array with the given message object + /// as sole element. This means that the extracted message will always be passed + /// into a single method argument, even if it is an array, with the target + /// method having a corresponding single argument of the array's type declared.

+ ///

This can be overridden to treat special message content such as arrays + /// differently, for example passing in each element of the message array + /// as distinct method argument.

+ ///
+ /// The converted message. + /// the array of arguments to be passed into the + /// listener method (each element of the array corresponding + /// to a distinct method argument) + protected virtual object[] BuildListenerArguments(object convertedMessage) + { + return new object[] { convertedMessage }; + } + + /// + /// Invokes the specified listener method. This default implementation can only handle invoking a + /// single argument method. + /// + /// Name of the listener method. + /// The arguments to be passed in. Only the first argument in the list is currently + /// supported in this implementation. + /// The result returned from the listener method + protected virtual object InvokeListenerMethod(string methodName, object[] arguments) + { + IDictionary vars = new Hashtable(); + vars["convertedObject"] = arguments[0]; + if (methodName.CompareTo(DefaultHandlerMethod) != 0) + { + //This is just to handle the case of overriding GetHandlerMethodName in a subclass and nothing else. + return ExpressionEvaluator.GetValue(handlerObject, methodName + "(#convertedObject)", vars); + } + // the normal case of using the cached expression. + return processingExpression.GetValue(handlerObject, vars); + } + /// /// Initialize the default implementations for the adapter's strategies. /// protected virtual void InitDefaultStrategies() { - processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)"); + ProcessingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)"); } /// @@ -436,7 +495,7 @@ namespace Spring.Messaging.Listener return message; } - private void HandleResult(object result, Message request) + protected virtual void HandleResult(object result, Message request) { if (logger.IsDebugEnabled) { diff --git a/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj b/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj index 2939aa33..de25947f 100644 --- a/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj +++ b/src/Spring/Spring.Messaging/Spring.Messaging.2008.csproj @@ -49,6 +49,7 @@ + diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/Adapter/StandardReflectionMessageListenerAdapter.cs b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/Adapter/StandardReflectionMessageListenerAdapter.cs new file mode 100644 index 00000000..7fe63a4c --- /dev/null +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/Adapter/StandardReflectionMessageListenerAdapter.cs @@ -0,0 +1,69 @@ +#region License + +/* + * Copyright 2002-2010 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 + +using System; +using System.Reflection; +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Messaging.Listener.Adapter +{ + /// + /// POC for a standard reflection based listener method invoker. + /// + /// Mark Pollack + public class StandardReflectionMessageListenerAdapter : MessageListenerAdapter + { + private const BindingFlags BINDING_FLAGS + = BindingFlags.Public | BindingFlags.NonPublic + | BindingFlags.Instance | BindingFlags.Static + | BindingFlags.IgnoreCase; + + protected override object InvokeListenerMethod(string methodName, object[] arguments) + { + try + { + MethodInfo mi = MethodNode.GetBestMethod(HandlerObject.GetType(), methodName, BINDING_FLAGS, arguments); + if (mi == null) + { + throw new ListenerExecutionFailedException("Failed to invoke the target method '" + methodName + + "' with arguments " + + StringUtils.ArrayToCommaDelimitedString(arguments)); + } + try + { + return mi.Invoke(HandlerObject, arguments); + } catch (Exception e) + { + throw new ListenerExecutionFailedException("Listener method '" + methodName + "' threw exception.", e); + } + } catch (Exception e) + { + throw new ListenerExecutionFailedException("Failed to invoke the target method '" + methodName + + "' with arguments " + + StringUtils.ArrayToCommaDelimitedString(arguments), e); + } + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.xml b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.xml index d1bbbc1c..75cdd012 100644 --- a/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.xml +++ b/test/Spring/Spring.Messaging.Tests/Messaging/Listener/NonTransactionalMessageListenerContainerTests.xml @@ -69,7 +69,10 @@ + + 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 ca0c7954..fe83a0bb 100644 --- a/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2008.csproj +++ b/test/Spring/Spring.Messaging.Tests/Spring.Messaging.Tests.2008.csproj @@ -90,6 +90,7 @@ +