SPRNET-1363 - Refactor OnMessage processing in MessageListenerAdapter to be better support listener method dispatching logic in subclasses.
This commit is contained in:
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception to be thrown when the execution of a listener method failed.
|
||||
/// </summary>
|
||||
/// <author>Juergen Hoeller</author>
|
||||
/// <author>Mark Pollack (.NET)</author>
|
||||
public class ListenerExecutionFailedException : MessagingException
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ListenerExecutionFailedException"/> class, with the specified message
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
public ListenerExecutionFailedException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ListenerExecutionFailedException"/> class, with the specified message
|
||||
/// and root cause exception
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="innerException">The inner exception.</param>
|
||||
public ListenerExecutionFailedException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the processing expression for use in custom subclasses
|
||||
/// </summary>
|
||||
/// <value>The processing expression.</value>
|
||||
protected IExpression ProcessingExpression
|
||||
{
|
||||
get { return processingExpression; }
|
||||
set { processingExpression = value; }
|
||||
}
|
||||
|
||||
#region IInitializingObject Members
|
||||
|
||||
/// <summary>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds an array of arguments to be passed into the taret listener method.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Allows for multiple method arguments to be built from a single message object.
|
||||
/// <p>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 <i>single</i> method argument, even if it is an array, with the target
|
||||
/// method having a corresponding single argument of the array's type declared.</p>
|
||||
/// <p>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.</p>
|
||||
/// </remarks>
|
||||
/// <param name="convertedMessage">The converted message.</param>
|
||||
/// <returns>the array of arguments to be passed into the
|
||||
/// listener method (each element of the array corresponding
|
||||
/// to a distinct method argument)</returns>
|
||||
protected virtual object[] BuildListenerArguments(object convertedMessage)
|
||||
{
|
||||
return new object[] { convertedMessage };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified listener method. This default implementation can only handle invoking a
|
||||
/// single argument method.
|
||||
/// </summary>
|
||||
/// <param name="methodName">Name of the listener method.</param>
|
||||
/// <param name="arguments">The arguments to be passed in. Only the first argument in the list is currently
|
||||
/// supported in this implementation.</param>
|
||||
/// <returns>The result returned from the listener method</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default implementations for the adapter's strategies.
|
||||
/// </summary>
|
||||
protected virtual void InitDefaultStrategies()
|
||||
{
|
||||
processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
|
||||
ProcessingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="Messaging\Core\MessageQueueMetadata.cs" />
|
||||
<Compile Include="Messaging\Core\MessageQueueMetadataCache.cs" />
|
||||
<Compile Include="Messaging\Listener\ListenerExecutionFailedException.cs" />
|
||||
<Compile Include="Messaging\Support\Converters\MessageConverterCreatorDelegate.cs" />
|
||||
<Compile Include="Messaging\Core\DefaultMessageQueueFactory.cs" />
|
||||
<Compile Include="Messaging\Support\MessageQueueCreatorDelegate.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
|
||||
{
|
||||
/// <summary>
|
||||
/// POC for a standard reflection based listener method invoker.
|
||||
/// </summary>
|
||||
/// <author>Mark Pollack</author>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,10 @@
|
||||
|
||||
|
||||
<!-- Adapter -->
|
||||
<!--
|
||||
<object id="messageListenerAdapter" type="Spring.Messaging.Listener.MessageListenerAdapter, Spring.Messaging">
|
||||
-->
|
||||
<object id="messageListenerAdapter" type="Spring.Messaging.Listener.Adapter.StandardReflectionMessageListenerAdapter, Spring.Messaging.Tests">
|
||||
<property name="DefaultResponseQueueName" value="msmqTestResponseQueue"/>
|
||||
<property name="MessageConverterObjectName" value="binaryMessageConverter"/>
|
||||
<property name="HandlerObject" ref="simpleHandler"/>
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="MessagingCompilerOptionsTests.cs" />
|
||||
<Compile Include="MessagingExceptionTests.cs" />
|
||||
<Compile Include="Messaging\Listener\Adapter\StandardReflectionMessageListenerAdapter.cs" />
|
||||
<Compile Include="Messaging\Listener\WaitingHandler.cs" />
|
||||
<Compile Include="Messaging\Core\MessageQueueMetadataCacheTests.cs" />
|
||||
<Compile Include="Messaging\Core\MessageQueueUtils.cs" />
|
||||
|
||||
Reference in New Issue
Block a user