Sync TIBCO EMS implementation with Apache NMS

This commit is contained in:
markpollack
2009-04-21 04:02:28 +00:00
parent d9a8566048
commit 94cd818bb4
14 changed files with 591 additions and 94 deletions

View File

@@ -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;
}
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -273,6 +273,9 @@ namespace Spring.Messaging.Ems.Connections
{
ConnectionFactoryUtils.ReleaseConnection(connection, connectionFactory, true);
}
this.connections.Clear();
this.sessions.Clear();
this.sessionsPerConnection.Clear();
}
/// <summary>

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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>