Sync TIBCO EMS implementation with Apache NMS
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
|
||||
using System;
|
||||
using System.Xml;
|
||||
using Spring.Core.TypeResolution;
|
||||
using TIBCO.EMS;
|
||||
using Spring.Core.TypeConversion;
|
||||
using Spring.Messaging.Ems.Listener;
|
||||
@@ -89,7 +90,11 @@ namespace Spring.Messaging.Ems.Config
|
||||
|
||||
private readonly string CONNECTION_FACTORY_ATTRIBUTE = "connection-factory";
|
||||
|
||||
private readonly string CONTAINER_CUSTOM_TYPE = "container-custom-type";
|
||||
|
||||
private readonly string PUBSUB_DOMAIN_ATTRIBUTE = "pubsub-domain";
|
||||
|
||||
private readonly string AUTO_STARTUP = "auto-startup";
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -201,13 +206,44 @@ namespace Spring.Messaging.Ems.Config
|
||||
private ObjectDefinitionBuilder ParseContainer(XmlElement listenerElement, XmlElement containerElement,
|
||||
ParserContext parserContext)
|
||||
{
|
||||
//Only support SimpleMessageListenerContainer or container-custom-type
|
||||
|
||||
Type containerType = typeof(SimpleMessageListenerContainer);
|
||||
if (containerElement.HasAttribute(CONTAINER_CUSTOM_TYPE))
|
||||
{
|
||||
string customType = containerElement.GetAttribute(CONTAINER_CUSTOM_TYPE);
|
||||
if (!StringUtils.HasLength(customType))
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(containerElement, CONTAINER_CUSTOM_TYPE,
|
||||
"Listener container '" + CONTAINER_CUSTOM_TYPE +
|
||||
"' attribute contains empty value.");
|
||||
}
|
||||
try
|
||||
{
|
||||
containerType = TypeResolutionUtils.ResolveType(customType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(containerElement, CONTAINER_CUSTOM_TYPE,
|
||||
"Invalid container-custom-type value [" + customType + "]", ex);
|
||||
}
|
||||
}
|
||||
//Only support SimpleMessageListenerContainer
|
||||
ObjectDefinitionBuilder containerDef =
|
||||
parserContext.ParserHelper.CreateRootObjectDefinitionBuilder(typeof (SimpleMessageListenerContainer));
|
||||
parserContext.ParserHelper.CreateRootObjectDefinitionBuilder(containerType);
|
||||
|
||||
ParseListenerConfiguration(listenerElement, parserContext, containerDef);
|
||||
ParseContainerConfiguration(containerElement, parserContext, containerDef);
|
||||
|
||||
if (containerElement.HasAttribute(AUTO_STARTUP))
|
||||
{
|
||||
string autoStartup = containerElement.GetAttribute(AUTO_STARTUP);
|
||||
if (!StringUtils.HasText(autoStartup))
|
||||
{
|
||||
containerDef.AddPropertyValue("AutoStartup", autoStartup);
|
||||
}
|
||||
}
|
||||
|
||||
string connectionFactoryObjectName = "connectionFactory";
|
||||
if (containerElement.HasAttribute(CONNECTION_FACTORY_ATTRIBUTE))
|
||||
{
|
||||
@@ -236,7 +272,7 @@ namespace Spring.Messaging.Ems.Config
|
||||
containerDef.AddPropertyValue("SessionAcknowledgeMode", acknowledgementMode);
|
||||
}
|
||||
|
||||
int[] concurrency = ParseConcurrency(containerElement, parserContext);
|
||||
string concurrency = ParseConcurrency(containerElement, parserContext);
|
||||
if (concurrency != null)
|
||||
{
|
||||
containerDef.AddPropertyValue("ConcurrentConsumers", concurrency[1]);
|
||||
@@ -358,60 +394,42 @@ namespace Spring.Messaging.Ems.Config
|
||||
return Session.AUTO_ACKNOWLEDGE;
|
||||
}
|
||||
|
||||
private int[] ParseConcurrency(XmlElement ele, ParserContext parserContext)
|
||||
private string ParseConcurrency(XmlElement ele, ParserContext parserContext)
|
||||
{
|
||||
string concurrency = ele.GetAttribute(CONCURRENCY_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(concurrency))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
else
|
||||
{
|
||||
return new int[] {1, Int32.Parse(concurrency)};
|
||||
return concurrency;
|
||||
}
|
||||
}
|
||||
|
||||
private string ParseRecoveryInterval(XmlElement ele, ParserContext parserContext)
|
||||
{
|
||||
string recoveryInterval = ele.GetAttribute(RECOVERY_INTERVAL_ATTRIBUTE);
|
||||
if (StringUtils.HasText(recoveryInterval))
|
||||
{
|
||||
return recoveryInterval;
|
||||
|
||||
}
|
||||
catch (FormatException ex)
|
||||
else
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(ele, CONCURRENCY_ATTRIBUTE,
|
||||
"Invalid concurrency value [" + concurrency + "]: only " +
|
||||
"integer (e.g. \"5\") values upported.", ex);
|
||||
return null;
|
||||
return SimpleMessageListenerContainer.DEFAULT_RECOVERY_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
private TimeSpan ParseRecoveryInterval(XmlElement ele, ParserContext parserContext)
|
||||
{
|
||||
string recoveryInterval = ele.GetAttribute(RECOVERY_INTERVAL_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(recoveryInterval))
|
||||
{
|
||||
return SimpleMessageListenerContainer.DEFAULT_RECOVERY_INTERVAL;
|
||||
}
|
||||
try
|
||||
{
|
||||
TimeSpanConverter tsc = new TimeSpanConverter();
|
||||
return (TimeSpan)tsc.ConvertFrom(recoveryInterval);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(ele, RECOVERY_INTERVAL_ATTRIBUTE,
|
||||
"Invalid recovery-interval value [" + recoveryInterval + "]", ex);
|
||||
return SimpleMessageListenerContainer.DEFAULT_RECOVERY_INTERVAL;
|
||||
}
|
||||
}
|
||||
private TimeSpan ParseMaxRecoveryTime(XmlElement ele, ParserContext parserContext)
|
||||
private string ParseMaxRecoveryTime(XmlElement ele, ParserContext parserContext)
|
||||
{
|
||||
string recoverTime = ele.GetAttribute(MAX_RECOVERY_TIME_ATTRIBUTE);
|
||||
if (!StringUtils.HasText(recoverTime))
|
||||
if (StringUtils.HasText(recoverTime))
|
||||
{
|
||||
return SimpleMessageListenerContainer.DEFAULT_MAX_RECOVERY_TIME;
|
||||
return recoverTime;
|
||||
}
|
||||
try
|
||||
else
|
||||
{
|
||||
TimeSpanConverter tsc = new TimeSpanConverter();
|
||||
return (TimeSpan)tsc.ConvertFrom(recoverTime);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parserContext.ReaderContext.ReportException(ele, MAX_RECOVERY_TIME_ATTRIBUTE,
|
||||
"Invalid max-recovery-time value [" + recoverTime + "]", ex);
|
||||
return SimpleMessageListenerContainer.DEFAULT_MAX_RECOVERY_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,26 @@
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="connection-factory" type="xsd:string" default="ConnectionFactory">
|
||||
<xsd:attribute name="container-custom-type" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
A custom listener container implementation class as fully qualified type name.
|
||||
Default is Spring's SimpleMessageListenerContainer.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="auto-startup" type="xsd:boolean" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Set whether to automatically start the listeners after initialization. Default is true, optionally set to false.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="connection-factory" type="xsd:string" default="ConnectionFactory">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to the ConnectionFactory object.
|
||||
|
||||
@@ -198,8 +198,8 @@ namespace Spring.Messaging.Ems.Connections
|
||||
if (resourceHolderToUse != resourceHolder)
|
||||
{
|
||||
TransactionSynchronizationManager.RegisterSynchronization(
|
||||
new EmsResourceSynchronization(resourceKey, resourceHolderToUse,
|
||||
resourceFactory.SynchedLocalTransactionAllowed));
|
||||
new EmsResourceSynchronization(resourceHolderToUse,
|
||||
resourceKey, resourceFactory.SynchedLocalTransactionAllowed));
|
||||
resourceHolderToUse.SynchronizedWithTransaction = true;
|
||||
TransactionSynchronizationManager.BindResource(resourceKey, resourceHolderToUse);
|
||||
}
|
||||
@@ -316,7 +316,7 @@ namespace Spring.Messaging.Ems.Connections
|
||||
|
||||
private bool holderActive = true;
|
||||
|
||||
public EmsResourceSynchronization(object resourceKey, EmsResourceHolder resourceHolder, bool transacted)
|
||||
public EmsResourceSynchronization(EmsResourceHolder resourceHolder, object resourceKey, bool transacted)
|
||||
{
|
||||
this.resourceKey = resourceKey;
|
||||
this.resourceHolder = resourceHolder;
|
||||
|
||||
@@ -273,6 +273,9 @@ namespace Spring.Messaging.Ems.Connections
|
||||
{
|
||||
ConnectionFactoryUtils.ReleaseConnection(connection, connectionFactory, true);
|
||||
}
|
||||
this.connections.Clear();
|
||||
this.sessions.Clear();
|
||||
this.sessionsPerConnection.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Spring.Messaging.Ems.Core
|
||||
|
||||
#endregion
|
||||
|
||||
private EmsTemplate jmsTemplate;
|
||||
private EmsTemplate emsTemplate;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -53,8 +53,8 @@ namespace Spring.Messaging.Ems.Core
|
||||
/// <value>The EMS template.</value>
|
||||
public EmsTemplate EmsTemplate
|
||||
{
|
||||
get { return jmsTemplate; }
|
||||
set { jmsTemplate = value; }
|
||||
get { return emsTemplate; }
|
||||
set { emsTemplate = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,11 +66,11 @@ namespace Spring.Messaging.Ems.Core
|
||||
{
|
||||
get
|
||||
{
|
||||
return (jmsTemplate != null ? this.jmsTemplate.ConnectionFactory : null);
|
||||
return (emsTemplate != null ? this.emsTemplate.ConnectionFactory : null);
|
||||
}
|
||||
set
|
||||
{
|
||||
this.jmsTemplate = CreateEmsTemplate(value);
|
||||
this.emsTemplate = CreateEmsTemplate(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Spring.Messaging.Ems.Core
|
||||
/// </summary>
|
||||
public virtual void AfterPropertiesSet()
|
||||
{
|
||||
if (jmsTemplate == null)
|
||||
if (emsTemplate == null)
|
||||
{
|
||||
throw new ArgumentException("connectionFactory or jmsTemplate is required");
|
||||
}
|
||||
|
||||
@@ -45,11 +45,11 @@ namespace Spring.Messaging.Ems.Listener
|
||||
///
|
||||
/// </remarks>
|
||||
/// <author>Mark Pollack</author>
|
||||
public abstract class AbstractEmsListeningContainer : EmsDestinationAccessor, ILifecycle, IObjectNameAware, IDisposable
|
||||
public abstract class AbstractListenerContainer : EmsDestinationAccessor, ILifecycle, IObjectNameAware, IDisposable
|
||||
{
|
||||
#region Logging
|
||||
|
||||
private readonly ILog logger = LogManager.GetLogger(typeof(AbstractEmsListeningContainer));
|
||||
private readonly ILog logger = LogManager.GetLogger(typeof(AbstractListenerContainer));
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Spring.Messaging.Ems.Listener
|
||||
/// a standard EMS MessageListener or a Spring-specific
|
||||
/// <see cref="ISessionAwareMessageListener"/>
|
||||
/// </summary>
|
||||
public abstract class AbstractMessageListenerContainer : AbstractEmsListeningContainer
|
||||
public abstract class AbstractMessageListenerContainer : AbstractListenerContainer
|
||||
{
|
||||
#region Logging
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ namespace Spring.Messaging.Ems.Listener.Adapter
|
||||
/// </summary>
|
||||
public static string ORIGINAL_DEFAULT_HANDLER_METHOD = "HandleMessage";
|
||||
|
||||
#region Fields
|
||||
|
||||
private object handlerObject;
|
||||
|
||||
private string defaultHandlerMethod = ORIGINAL_DEFAULT_HANDLER_METHOD;
|
||||
@@ -70,6 +72,10 @@ namespace Spring.Messaging.Ems.Listener.Adapter
|
||||
|
||||
private IMessageConverter messageConverter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MessageListenerAdapter"/> class with default settings.
|
||||
/// </summary>
|
||||
@@ -77,7 +83,6 @@ namespace Spring.Messaging.Ems.Listener.Adapter
|
||||
{
|
||||
InitDefaultStrategies();
|
||||
handlerObject = this;
|
||||
processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -90,6 +95,10 @@ namespace Spring.Messaging.Ems.Listener.Adapter
|
||||
this.handlerObject = handlerObject;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler object to delegate message listening to.
|
||||
/// </summary>
|
||||
@@ -118,6 +127,7 @@ namespace Spring.Messaging.Ems.Listener.Adapter
|
||||
set
|
||||
{
|
||||
defaultHandlerMethod = value;
|
||||
processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +208,8 @@ namespace Spring.Messaging.Ems.Listener.Adapter
|
||||
set { messageConverter = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Standard JMS {@link MessageListener} entry point.
|
||||
@@ -267,11 +279,6 @@ namespace Spring.Messaging.Ems.Listener.Adapter
|
||||
IDictionary vars = new Hashtable();
|
||||
vars["convertedObject"] = convertedMessage;
|
||||
|
||||
//Need to parse each time since have overloaded methods and
|
||||
//expression processor caches target of first invocation.
|
||||
//TODO - check JIRA as I believe this has been fixed, otherwise, use regular reflection. -MLP
|
||||
processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
|
||||
|
||||
//Invoke message handler method and get result.
|
||||
object result;
|
||||
try
|
||||
@@ -317,6 +324,7 @@ namespace Spring.Messaging.Ems.Listener.Adapter
|
||||
protected virtual void InitDefaultStrategies()
|
||||
{
|
||||
MessageConverter = new SimpleMessageConverter();
|
||||
processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -442,7 +450,7 @@ namespace Spring.Messaging.Ems.Listener.Adapter
|
||||
/// <para>The default implementation first checks the JMS Reply-To
|
||||
/// Destination of the supplied request; if that is not <code>null</code>
|
||||
/// it is returned; if it is <code>null</code>, then the configured
|
||||
/// <see cref="ResolveDefaultResponseDestination"/> default response destination}
|
||||
/// <see cref="DefaultResponseDestination"/> default response destination}
|
||||
/// is returned; if this too is <code>null</code>, then an
|
||||
/// <see cref="InvalidDestinationException"/>is thrown.
|
||||
/// </para>
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace Spring.Messaging.Ems.Listener
|
||||
/// MessageConsumer.Listener method to create concurrent
|
||||
/// MessageConsumers for the specified listeners.
|
||||
/// </summary>
|
||||
/// <author>Juergen Hoeller</author>
|
||||
/// <author>Mark Pollack (.NET)</author>
|
||||
public class SimpleMessageListenerContainer : AbstractMessageListenerContainer, IExceptionListener
|
||||
{
|
||||
#region Logging
|
||||
@@ -47,12 +49,12 @@ namespace Spring.Messaging.Ems.Listener
|
||||
/// <summary>
|
||||
/// The default recovery time interval between connection reconnection attempts
|
||||
/// </summary>
|
||||
public static TimeSpan DEFAULT_RECOVERY_INTERVAL = new TimeSpan(0, 0, 0, 5, 0);
|
||||
public static string DEFAULT_RECOVERY_INTERVAL = "5s";
|
||||
|
||||
/// <summary>
|
||||
/// The total time connection recovery will be attempted.
|
||||
/// </summary>
|
||||
public static TimeSpan DEFAULT_MAX_RECOVERY_TIME = new TimeSpan(0, 0, 10, 0, 0);
|
||||
public static string DEFAULT_MAX_RECOVERY_TIME = "10m";
|
||||
|
||||
private bool pubSubNoLocal = false;
|
||||
|
||||
@@ -64,9 +66,9 @@ namespace Spring.Messaging.Ems.Listener
|
||||
|
||||
private object consumersMonitor = new object();
|
||||
|
||||
private TimeSpan recoveryInterval = DEFAULT_RECOVERY_INTERVAL;
|
||||
private TimeSpan recoveryInterval = new TimeSpan(0, 0, 0, 5, 0);
|
||||
|
||||
private TimeSpan maxRecoveryTime = DEFAULT_MAX_RECOVERY_TIME;
|
||||
private TimeSpan maxRecoveryTime = new TimeSpan(0, 0, 10, 0, 0);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -318,18 +320,27 @@ namespace Spring.Messaging.Ems.Listener
|
||||
/// <throws>EMSException if destruction failed </throws>
|
||||
protected override void DoShutdown()
|
||||
{
|
||||
logger.Debug("Closing EMS MessageConsumers");
|
||||
foreach (MessageConsumer messageConsumer in consumers)
|
||||
lock (consumersMonitor)
|
||||
{
|
||||
EmsUtils.CloseMessageConsumer(messageConsumer);
|
||||
if (consumers != null)
|
||||
{
|
||||
logger.Debug("Closing NMS MessageConsumers");
|
||||
foreach (MessageConsumer messageConsumer in consumers)
|
||||
{
|
||||
EmsUtils.CloseMessageConsumer(messageConsumer);
|
||||
}
|
||||
}
|
||||
if (sessions != null)
|
||||
{
|
||||
logger.Debug("Closing NMS Sessions");
|
||||
foreach (Session session in sessions)
|
||||
{
|
||||
EmsUtils.CloseSession(session);
|
||||
}
|
||||
}
|
||||
consumers = null;
|
||||
sessions = null;
|
||||
}
|
||||
logger.Debug("Closing EMS Sessions");
|
||||
foreach (Session session in sessions)
|
||||
{
|
||||
EmsUtils.CloseSession(session);
|
||||
}
|
||||
consumers = null;
|
||||
sessions = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#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.Ems.Support.Converter
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a layer of indirection when adding the 'type' of the object as a message property.
|
||||
/// </summary>
|
||||
/// <author>Mark Pollack</author>
|
||||
public interface ITypeMapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the field in the message that has type information..
|
||||
/// </summary>
|
||||
/// <value>The name of the type id field.</value>
|
||||
string TypeIdFieldName
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from a type to a string.
|
||||
/// </summary>
|
||||
/// <param name="typeOfObjectToConvert">The type of object to convert.</param>
|
||||
/// <returns></returns>
|
||||
string FromType(Type typeOfObjectToConvert);
|
||||
|
||||
/// <summary>
|
||||
/// Convert from a string to a type
|
||||
/// </summary>
|
||||
/// <param name="typeId">The type id.</param>
|
||||
/// <returns></returns>
|
||||
Type ToType(string typeId);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.Serialization;
|
||||
using TIBCO.EMS;
|
||||
|
||||
namespace Spring.Messaging.Ems.Support.Converter
|
||||
@@ -56,27 +55,23 @@ namespace Spring.Messaging.Ems.Support.Converter
|
||||
{
|
||||
return (Message) objectToConvert;
|
||||
}
|
||||
else if (objectToConvert is string)
|
||||
if (objectToConvert is string)
|
||||
{
|
||||
return CreateMessageForString((string) objectToConvert, session);
|
||||
}
|
||||
else if (objectToConvert is sbyte[])
|
||||
if (objectToConvert is sbyte[])
|
||||
{
|
||||
return CreateMessageForByteArray((byte[]) objectToConvert, session);
|
||||
}
|
||||
else if (objectToConvert is IDictionary)
|
||||
if (objectToConvert is IDictionary)
|
||||
{
|
||||
return CreateMessageForMap((IDictionary) objectToConvert, session);
|
||||
}
|
||||
else if (objectToConvert is ISerializable)
|
||||
if (objectToConvert != null && objectToConvert.GetType().IsSerializable)
|
||||
{
|
||||
return
|
||||
CreateMessageForSerializable(((ISerializable) objectToConvert), session);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MessageConversionException("Cannot convert object [" + objectToConvert + "] to EMS message");
|
||||
return CreateMessageForSerializable(objectToConvert, session);
|
||||
}
|
||||
throw new MessageConversionException("Cannot convert object [" + objectToConvert + "] to EMS message");
|
||||
}
|
||||
|
||||
/// <summary> Convert from a EMS Message to a .NET object.</summary>
|
||||
@@ -91,22 +86,19 @@ namespace Spring.Messaging.Ems.Support.Converter
|
||||
{
|
||||
return ExtractStringFromMessage((TextMessage) messageToConvert);
|
||||
}
|
||||
else if (messageToConvert is BytesMessage)
|
||||
if (messageToConvert is BytesMessage)
|
||||
{
|
||||
return ExtractByteArrayFromMessage((BytesMessage) messageToConvert);
|
||||
}
|
||||
else if (messageToConvert is MapMessage)
|
||||
if (messageToConvert is MapMessage)
|
||||
{
|
||||
return ExtractMapFromMessage((MapMessage) messageToConvert);
|
||||
}
|
||||
else if (messageToConvert is ObjectMessage)
|
||||
if (messageToConvert is ObjectMessage)
|
||||
{
|
||||
return ExtractSerializableFromMessage((ObjectMessage) messageToConvert);
|
||||
}
|
||||
else
|
||||
{
|
||||
return messageToConvert;
|
||||
}
|
||||
return messageToConvert;
|
||||
}
|
||||
|
||||
#region To Converter Methods
|
||||
@@ -172,7 +164,7 @@ namespace Spring.Messaging.Ems.Support.Converter
|
||||
/// </returns>
|
||||
/// <throws> EMSException if thrown by EMS methods </throws>
|
||||
protected virtual ObjectMessage CreateMessageForSerializable(
|
||||
ISerializable objectToSend, Session session)
|
||||
object objectToSend, Session session)
|
||||
{
|
||||
return session.CreateObjectMessage(objectToSend);
|
||||
}
|
||||
@@ -232,7 +224,7 @@ namespace Spring.Messaging.Ems.Support.Converter
|
||||
protected virtual object ExtractSerializableFromMessage(
|
||||
ObjectMessage message)
|
||||
{
|
||||
return message.TheObject as ISerializable;
|
||||
return message.TheObject;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright <20> 2002-2006 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 System.Collections;
|
||||
using Spring.Core.TypeResolution;
|
||||
|
||||
namespace Spring.Messaging.Ems.Support.Converter
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a layer of indirection when adding the 'type' of the object as a message property.
|
||||
/// </summary>
|
||||
public class TypeMapper : ITypeMapper
|
||||
{
|
||||
private string defaultNamespace;
|
||||
private string defaultAssemblyName;
|
||||
|
||||
//Generics not used to support both 1.1 and 2.0
|
||||
private IDictionary idTypeMapping;
|
||||
private IDictionary typeIdMapping;
|
||||
|
||||
private string defaultHashtableTypeId = "Hashtable";
|
||||
|
||||
private Type defaultHashtableClass = typeof(Hashtable);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypeMapper"/>
|
||||
/// </summary>
|
||||
public TypeMapper()
|
||||
{
|
||||
idTypeMapping = new Hashtable();
|
||||
typeIdMapping = new Hashtable();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id type mapping.
|
||||
/// </summary>
|
||||
/// <value>The id type mapping.</value>
|
||||
public IDictionary IdTypeMapping
|
||||
{
|
||||
get { return idTypeMapping; }
|
||||
set { idTypeMapping = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the field in the message that has type information..
|
||||
/// </summary>
|
||||
/// <value>The name of the type id field.</value>
|
||||
public string TypeIdFieldName
|
||||
{
|
||||
get { return "__TypeId__"; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default hashtable class.
|
||||
/// </summary>
|
||||
/// <value>The default hashtable class.</value>
|
||||
public Type DefaultHashtableClass
|
||||
{
|
||||
set { defaultHashtableClass = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from a type to a string.
|
||||
/// </summary>
|
||||
/// <param name="typeOfObjectToConvert">The type of object to convert.</param>
|
||||
/// <returns></returns>
|
||||
public string FromType(Type typeOfObjectToConvert)
|
||||
{
|
||||
|
||||
if (typeIdMapping.Contains(typeOfObjectToConvert))
|
||||
{
|
||||
return typeIdMapping[typeOfObjectToConvert] as string;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( typeof (IDictionary).IsAssignableFrom(typeOfObjectToConvert))
|
||||
{
|
||||
return defaultHashtableTypeId;
|
||||
}
|
||||
return typeOfObjectToConvert.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from a string to a type
|
||||
/// </summary>
|
||||
/// <param name="typeId">The type id.</param>
|
||||
/// <returns></returns>
|
||||
public Type ToType(string typeId)
|
||||
{
|
||||
if (idTypeMapping.Contains(typeId))
|
||||
{
|
||||
return idTypeMapping[typeId] as Type;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (typeId.Equals(defaultHashtableTypeId))
|
||||
{
|
||||
return defaultHashtableClass;
|
||||
}
|
||||
string fullyQualifiedTypeName = defaultNamespace + "." +
|
||||
typeId + ", " +
|
||||
DefaultAssemblyName;
|
||||
return TypeResolutionUtils.ResolveType(fullyQualifiedTypeName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default namespace.
|
||||
/// </summary>
|
||||
/// <value>The default namespace.</value>
|
||||
public string DefaultNamespace
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultNamespace;
|
||||
}
|
||||
set
|
||||
{
|
||||
defaultNamespace = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default name of the assembly.
|
||||
/// </summary>
|
||||
/// <value>The default name of the assembly.</value>
|
||||
public string DefaultAssemblyName
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultAssemblyName;
|
||||
}
|
||||
set
|
||||
{
|
||||
defaultAssemblyName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Afters the properties set.
|
||||
/// </summary>
|
||||
public void AfterPropertiesSet()
|
||||
{
|
||||
ValidateIdTypeMapping();
|
||||
if (DefaultAssemblyName != null && DefaultNamespace == null)
|
||||
{
|
||||
throw new ArgumentException("Default Namespace required when DefaultAssemblyName is set.");
|
||||
}
|
||||
if (DefaultNamespace != null && DefaultAssemblyName == null)
|
||||
{
|
||||
throw new ArgumentException("Default Assembly Name required when DefaultNamespace is set.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ValidateIdTypeMapping()
|
||||
{
|
||||
IDictionary finalIdTypeMapping = new Hashtable();
|
||||
foreach (DictionaryEntry entry in idTypeMapping)
|
||||
{
|
||||
string id = entry.Key.ToString();
|
||||
Type t = entry.Value as Type;
|
||||
if (t == null)
|
||||
{
|
||||
//convert from string value.
|
||||
string typeName = entry.Value.ToString();
|
||||
t = TypeResolutionUtils.ResolveType(typeName);
|
||||
}
|
||||
finalIdTypeMapping.Add(id,t);
|
||||
typeIdMapping.Add(t,id);
|
||||
|
||||
}
|
||||
idTypeMapping = finalIdTypeMapping;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using TIBCO.EMS;
|
||||
|
||||
namespace Spring.Messaging.Ems.Support.Converter
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert an object via XML serialization for sending via an ITextMessage
|
||||
/// </summary>
|
||||
/// <author>Mark Pollack</author>
|
||||
public class XmlMessageConverter : IMessageConverter
|
||||
{
|
||||
private IMessageConverter defaultMessageConverter = new SimpleMessageConverter();
|
||||
|
||||
|
||||
private ITypeMapper typeMapper;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type mapper.
|
||||
/// </summary>
|
||||
/// <value>The type mapper.</value>
|
||||
public ITypeMapper TypeMapper
|
||||
{
|
||||
set { typeMapper = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a .NET object to a NMS Message using the supplied session
|
||||
/// to create the message object.
|
||||
/// </summary>
|
||||
/// <param name="objectToConvert">the object to convert</param>
|
||||
/// <param name="session">the Session to use for creating a NMS Message</param>
|
||||
/// <returns>the NMS Message</returns>
|
||||
/// <throws>NMSException if thrown by NMS API methods </throws>
|
||||
/// <throws>MessageConversionException in case of conversion failure </throws>
|
||||
public Message ToMessage(object objectToConvert, Session session)
|
||||
{
|
||||
if (objectToConvert == null)
|
||||
{
|
||||
throw new MessageConversionException("Can't convert null object");
|
||||
}
|
||||
try
|
||||
{
|
||||
if (objectToConvert.GetType().Equals(typeof(string)) ||
|
||||
typeof(IDictionary).IsAssignableFrom(objectToConvert.GetType()) ||
|
||||
objectToConvert.GetType().Equals(typeof(Byte[])))
|
||||
{
|
||||
return defaultMessageConverter.ToMessage(objectToConvert, session);
|
||||
}
|
||||
else
|
||||
{
|
||||
string xmlString = GetXmlString(objectToConvert);
|
||||
Message msg = session.CreateTextMessage(xmlString);
|
||||
msg.SetStringProperty(typeMapper.TypeIdFieldName, typeMapper.FromType(objectToConvert.GetType()));
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new MessageConversionException("Can't convert object of type " + objectToConvert.GetType(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XML string for an object
|
||||
/// </summary>
|
||||
/// <param name="objectToConvert">The object to convert.</param>
|
||||
/// <returns>XML string</returns>
|
||||
protected virtual string GetXmlString(object objectToConvert)
|
||||
{
|
||||
string xmlString;
|
||||
XmlTextWriter xmlTextWriter = null;
|
||||
MemoryStream memoryStream = null;
|
||||
try
|
||||
{
|
||||
memoryStream = new MemoryStream();
|
||||
xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
|
||||
XmlSerializer xs = new XmlSerializer(objectToConvert.GetType());
|
||||
xs.Serialize(xmlTextWriter, objectToConvert);
|
||||
xmlString = UTF8ByteArrayToString(((MemoryStream)xmlTextWriter.BaseStream).ToArray());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new MessageConversionException("Can't convert object of type " + objectToConvert.GetType(), e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (memoryStream != null) memoryStream.Close();
|
||||
}
|
||||
return xmlString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert from a NMS Message to a .NET object.
|
||||
/// </summary>
|
||||
/// <param name="messageToConvert">the message to convert</param>
|
||||
/// <returns>the converted .NET object</returns>
|
||||
/// <throws>MessageConversionException in case of conversion failure </throws>
|
||||
public object FromMessage(Message messageToConvert)
|
||||
{
|
||||
if (messageToConvert == null)
|
||||
{
|
||||
throw new MessageConversionException("Can't convert null message");
|
||||
}
|
||||
try
|
||||
{
|
||||
string converterId = messageToConvert.GetStringProperty(typeMapper.TypeIdFieldName);
|
||||
if (converterId == null)
|
||||
{
|
||||
return defaultMessageConverter.FromMessage(messageToConvert);
|
||||
}
|
||||
else
|
||||
{
|
||||
TextMessage textMessage = messageToConvert as TextMessage;
|
||||
if (textMessage == null)
|
||||
{
|
||||
throw new MessageConversionException("Can't convert message of type " +
|
||||
messageToConvert.GetType());
|
||||
}
|
||||
|
||||
using (MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(textMessage.Text)))
|
||||
{
|
||||
|
||||
XmlSerializer xs = new XmlSerializer(GetTargetType(textMessage));
|
||||
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
|
||||
return xs.Deserialize(memoryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new MessageConversionException("Can't convert message of type " + messageToConvert.GetType(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the target given the message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <returns>Type of the target</returns>
|
||||
protected virtual Type GetTargetType(TextMessage message)
|
||||
{
|
||||
return typeMapper.ToType(message.GetStringProperty(typeMapper.TypeIdFieldName));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts a byte array to a UTF8 string.
|
||||
/// </summary>
|
||||
/// <param name="characters">The characters.</param>
|
||||
/// <returns>UTF8 string</returns>
|
||||
protected virtual String UTF8ByteArrayToString(Byte[] characters)
|
||||
{
|
||||
UTF8Encoding encoding = new UTF8Encoding();
|
||||
|
||||
String constructedString = encoding.GetString(characters);
|
||||
|
||||
return (constructedString);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts a UTF8 string to a byte array
|
||||
/// </summary>
|
||||
/// <param name="xmlString">The p XML string.</param>
|
||||
/// <returns></returns>
|
||||
protected virtual Byte[] StringToUTF8ByteArray(String xmlString)
|
||||
{
|
||||
UTF8Encoding encoding = new UTF8Encoding();
|
||||
|
||||
Byte[] byteArray = encoding.GetBytes(xmlString);
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
xmlns:nms="http://www.springframework.net/ems">
|
||||
|
||||
<nms:listener-container connection-factory="testConnectionFactory"
|
||||
destination-resolver="testDestinationResolver" message-converter="testMessageConverter">
|
||||
destination-resolver="testDestinationResolver" message-converter="testMessageConverter"
|
||||
auto-startup="false" concurrency="${concurrency}">
|
||||
<nms:listener id="listener1" destination="testDestination" ref="testObject1" method="SetName"/>
|
||||
<nms:listener id="listener2" destination="testDestination" ref="testObject2" method="SetName"
|
||||
response-destination="responseDestination"/>
|
||||
@@ -36,5 +37,15 @@
|
||||
|
||||
<object id="testObject3" type="Spring.Messaging.Ems.Connections.TestMessageListener, Spring.Messaging.Ems.Tests"/>
|
||||
|
||||
<object type="Spring.Objects.Factory.Config.VariablePlaceholderConfigurer, Spring.Core">
|
||||
<property name="VariableSources">
|
||||
<list>
|
||||
<object type="Spring.Objects.Factory.Config.ConfigSectionVariableSource, Spring.Core">
|
||||
<property name="SectionNames" value="NmsConfiguration" />
|
||||
</object>
|
||||
</list>
|
||||
</property>
|
||||
</object>
|
||||
|
||||
</objects>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user