diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/MessageListenerContainerObjectDefinitionParser.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/MessageListenerContainerObjectDefinitionParser.cs index a75ee825..25d76cc2 100644 --- a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/MessageListenerContainerObjectDefinitionParser.cs +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/MessageListenerContainerObjectDefinitionParser.cs @@ -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; } } diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/spring-ems-1.2.xsd b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/spring-ems-1.2.xsd index 397f3318..2364c72e 100644 --- a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/spring-ems-1.2.xsd +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/spring-ems-1.2.xsd @@ -40,7 +40,26 @@ - + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsGatewaySupport.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsGatewaySupport.cs index e87ca79d..7f22bd2c 100644 --- a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsGatewaySupport.cs +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsGatewaySupport.cs @@ -44,7 +44,7 @@ namespace Spring.Messaging.Ems.Core #endregion - private EmsTemplate jmsTemplate; + private EmsTemplate emsTemplate; /// @@ -53,8 +53,8 @@ namespace Spring.Messaging.Ems.Core /// The EMS template. public EmsTemplate EmsTemplate { - get { return jmsTemplate; } - set { jmsTemplate = value; } + get { return emsTemplate; } + set { emsTemplate = value; } } /// @@ -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 /// public virtual void AfterPropertiesSet() { - if (jmsTemplate == null) + if (emsTemplate == null) { throw new ArgumentException("connectionFactory or jmsTemplate is required"); } diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractemsListeningContainer.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractListenerContainer.cs similarity index 96% rename from src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractemsListeningContainer.cs rename to src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractListenerContainer.cs index 9387fd36..f0139757 100644 --- a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractemsListeningContainer.cs +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractListenerContainer.cs @@ -45,11 +45,11 @@ namespace Spring.Messaging.Ems.Listener /// /// /// Mark Pollack - 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 diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractMessageListenerContainer.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractMessageListenerContainer.cs index 9af88517..26c13ed1 100644 --- a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/AbstractMessageListenerContainer.cs @@ -32,7 +32,7 @@ namespace Spring.Messaging.Ems.Listener /// a standard EMS MessageListener or a Spring-specific /// /// - public abstract class AbstractMessageListenerContainer : AbstractEmsListeningContainer + public abstract class AbstractMessageListenerContainer : AbstractListenerContainer { #region Logging diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/Adapter/MessageListenerAdapter.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/Adapter/MessageListenerAdapter.cs index f895cec1..c10489e8 100644 --- a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/Adapter/MessageListenerAdapter.cs +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/Adapter/MessageListenerAdapter.cs @@ -58,6 +58,8 @@ namespace Spring.Messaging.Ems.Listener.Adapter /// 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 + /// /// Initializes a new instance of the class with default settings. /// @@ -77,7 +83,6 @@ namespace Spring.Messaging.Ems.Listener.Adapter { InitDefaultStrategies(); handlerObject = this; - processingExpression = Expression.Parse(defaultHandlerMethod + "(#convertedObject)"); } /// @@ -90,6 +95,10 @@ namespace Spring.Messaging.Ems.Listener.Adapter this.handlerObject = handlerObject; } + #endregion + + #region Properties + /// /// Gets or sets the handler object to delegate message listening to. /// @@ -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 + /// /// 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)"); } /// @@ -442,7 +450,7 @@ namespace Spring.Messaging.Ems.Listener.Adapter /// The default implementation first checks the JMS Reply-To /// Destination of the supplied request; if that is not null /// it is returned; if it is null, then the configured - /// default response destination} + /// default response destination} /// is returned; if this too is null, then an /// is thrown. /// diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/SimpleMessageListenerContainer.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/SimpleMessageListenerContainer.cs index 94517c5e..f58cac38 100644 --- a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/SimpleMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/SimpleMessageListenerContainer.cs @@ -34,6 +34,8 @@ namespace Spring.Messaging.Ems.Listener /// MessageConsumer.Listener method to create concurrent /// MessageConsumers for the specified listeners. /// + /// Juergen Hoeller + /// Mark Pollack (.NET) public class SimpleMessageListenerContainer : AbstractMessageListenerContainer, IExceptionListener { #region Logging @@ -47,12 +49,12 @@ namespace Spring.Messaging.Ems.Listener /// /// The default recovery time interval between connection reconnection attempts /// - public static TimeSpan DEFAULT_RECOVERY_INTERVAL = new TimeSpan(0, 0, 0, 5, 0); + public static string DEFAULT_RECOVERY_INTERVAL = "5s"; /// /// The total time connection recovery will be attempted. /// - 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 /// EMSException if destruction failed 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; } diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/ITypeMapper.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/ITypeMapper.cs new file mode 100644 index 00000000..cd6bb7cd --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/ITypeMapper.cs @@ -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 +{ + /// + /// Provides a layer of indirection when adding the 'type' of the object as a message property. + /// + /// Mark Pollack + public interface ITypeMapper + { + /// + /// Gets the name of the field in the message that has type information.. + /// + /// The name of the type id field. + string TypeIdFieldName + { + get; + } + + /// + /// Convert from a type to a string. + /// + /// The type of object to convert. + /// + string FromType(Type typeOfObjectToConvert); + + /// + /// Convert from a string to a type + /// + /// The type id. + /// + Type ToType(string typeId); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/SimpleMessageConverter.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/SimpleMessageConverter.cs index 27c692ff..a05c7eb2 100644 --- a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/SimpleMessageConverter.cs +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/SimpleMessageConverter.cs @@ -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"); } /// Convert from a EMS Message to a .NET object. @@ -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 /// /// EMSException if thrown by EMS methods 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 diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/TypeMapper.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/TypeMapper.cs new file mode 100644 index 00000000..a924bff4 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/TypeMapper.cs @@ -0,0 +1,200 @@ +#region License + +/* + * Copyright © 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 +{ + /// + /// Provides a layer of indirection when adding the 'type' of the object as a message property. + /// + 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); + + /// + /// Initializes a new instance of the + /// + public TypeMapper() + { + idTypeMapping = new Hashtable(); + typeIdMapping = new Hashtable(); + } + + + /// + /// Gets or sets the id type mapping. + /// + /// The id type mapping. + public IDictionary IdTypeMapping + { + get { return idTypeMapping; } + set { idTypeMapping = value; } + } + + /// + /// Gets the name of the field in the message that has type information.. + /// + /// The name of the type id field. + public string TypeIdFieldName + { + get { return "__TypeId__"; } + } + + + /// + /// Sets the default hashtable class. + /// + /// The default hashtable class. + public Type DefaultHashtableClass + { + set { defaultHashtableClass = value; } + } + + /// + /// Convert from a type to a string. + /// + /// The type of object to convert. + /// + 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; + } + } + + /// + /// Convert from a string to a type + /// + /// The type id. + /// + 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); + } + + } + + /// + /// Gets or sets the default namespace. + /// + /// The default namespace. + public string DefaultNamespace + { + get + { + return defaultNamespace; + } + set + { + defaultNamespace = value; + } + + } + + /// + /// Gets or sets the default name of the assembly. + /// + /// The default name of the assembly. + public string DefaultAssemblyName + { + get + { + return defaultAssemblyName; + } + set + { + defaultAssemblyName = value; + } + } + + /// + /// Afters the properties set. + /// + 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; + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/XmlMessageConverter.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/XmlMessageConverter.cs new file mode 100644 index 00000000..07fea792 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Support/Converter/XmlMessageConverter.cs @@ -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 +{ + /// + /// Convert an object via XML serialization for sending via an ITextMessage + /// + /// Mark Pollack + public class XmlMessageConverter : IMessageConverter + { + private IMessageConverter defaultMessageConverter = new SimpleMessageConverter(); + + + private ITypeMapper typeMapper; + + + /// + /// Sets the type mapper. + /// + /// The type mapper. + public ITypeMapper TypeMapper + { + set { typeMapper = value; } + } + + /// + /// Convert a .NET object to a NMS Message using the supplied session + /// to create the message object. + /// + /// the object to convert + /// the Session to use for creating a NMS Message + /// the NMS Message + /// NMSException if thrown by NMS API methods + /// MessageConversionException in case of conversion failure + 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); + } + } + + /// + /// Gets the XML string for an object + /// + /// The object to convert. + /// XML string + 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; + } + + /// + /// Convert from a NMS Message to a .NET object. + /// + /// the message to convert + /// the converted .NET object + /// MessageConversionException in case of conversion failure + 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); + } + } + + /// + /// Gets the type of the target given the message. + /// + /// The message. + /// Type of the target + protected virtual Type GetTargetType(TextMessage message) + { + return typeMapper.ToType(message.GetStringProperty(typeMapper.TypeIdFieldName)); + } + + + /// + /// Converts a byte array to a UTF8 string. + /// + /// The characters. + /// UTF8 string + protected virtual String UTF8ByteArrayToString(Byte[] characters) + { + UTF8Encoding encoding = new UTF8Encoding(); + + String constructedString = encoding.GetString(characters); + + return (constructedString); + } + + + /// + /// Converts a UTF8 string to a byte array + /// + /// The p XML string. + /// + protected virtual Byte[] StringToUTF8ByteArray(String xmlString) + { + UTF8Encoding encoding = new UTF8Encoding(); + + Byte[] byteArray = encoding.GetBytes(xmlString); + + return byteArray; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Config/EmsNamespaceHandlerTests.xml b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Config/EmsNamespaceHandlerTests.xml index 1fdf1da6..5e326b6a 100644 --- a/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Config/EmsNamespaceHandlerTests.xml +++ b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Config/EmsNamespaceHandlerTests.xml @@ -4,7 +4,8 @@ xmlns:nms="http://www.springframework.net/ems"> + destination-resolver="testDestinationResolver" message-converter="testMessageConverter" + auto-startup="false" concurrency="${concurrency}"> @@ -36,5 +37,15 @@ + + + + + + + + + +