diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/EmsNamespaceParser.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/EmsNamespaceParser.cs new file mode 100644 index 00000000..36a264ea --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/EmsNamespaceParser.cs @@ -0,0 +1,49 @@ +#region License + +/* + * Copyright 2002-2007 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 Spring.Objects.Factory.Xml; + +namespace Spring.Messaging.Ems.Config +{ + /// + /// Namespace parser for the nms namespace. + /// + /// Mark Fisher + /// Juergen Hoeller + /// Mark Pollack (.NET) + [ + NamespaceParser( + Namespace = "http://www.springframework.net/ems", + SchemaLocationAssemblyHint = typeof (EmsNamespaceParser), + SchemaLocation = "/Spring.Messaging.Ems.Config/spring-ems-1.2.xsd" + ) + ] + public class EmsNamespaceParser : NamespaceParserSupport + { + /// + /// Register a MessageListenerContainer for the 'listener-container' tag. + /// + public override void Init() + { + RegisterObjectDefinitionParser("listener-container", new MessageListenerContainerObjectDefinitionParser()); + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/MessageListenerContainerObjectDefinitionParser.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/MessageListenerContainerObjectDefinitionParser.cs new file mode 100644 index 00000000..d3c19c33 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/MessageListenerContainerObjectDefinitionParser.cs @@ -0,0 +1,414 @@ +#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; +using System.Xml; +using TIBCO.EMS; +using Spring.Core.TypeConversion; +using Spring.Messaging.Ems.Listener; +using Spring.Messaging.Ems.Listener.Adapter; +using Spring.Objects.Factory.Config; +using Spring.Objects.Factory.Support; +using Spring.Objects.Factory.Xml; +using Spring.Util; + +namespace Spring.Messaging.Ems.Config +{ + /// + /// Parser for the EMS <listener-container> element. + /// + /// Mark Fisher + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class MessageListenerContainerObjectDefinitionParser : IObjectDefinitionParser + { + #region Fields + + private readonly string LISTENER_ELEMENT = "listener"; + + private readonly string ID_ATTRIBUTE = "id"; + + private readonly string DESTINATION_ATTRIBUTE = "destination"; + + private readonly string SUBSCRIPTION_ATTRIBUTE = "subscription"; + + private readonly string SELECTOR_ATTRIBUTE = "selector"; + + private readonly string REF_ATTRIBUTE = "ref"; + + private readonly string METHOD_ATTRIBUTE = "method"; + + private readonly string DESTINATION_RESOLVER_ATTRIBUTE = "destination-resolver"; + + private readonly string MESSAGE_CONVERTER_ATTRIBUTE = "message-converter"; + + private readonly string RESPONSE_DESTINATION_ATTRIBUTE = "response-destination"; + + private readonly string DESTINATION_TYPE_ATTRIBUTE = "destination-type"; + + private readonly string DESTINATION_TYPE_QUEUE = "queue"; + + private readonly string DESTINATION_TYPE_TOPIC = "topic"; + + private readonly string DESTINATION_TYPE_DURABLE_TOPIC = "durableTopic"; + + private readonly string CLIENT_ID_ATTRIBUTE = "client-id"; + + private readonly string ACKNOWLEDGE_ATTRIBUTE = "acknowledge"; + + private readonly string ACKNOWLEDGE_AUTO = "auto"; + + private readonly string ACKNOWLEDGE_CLIENT = "client"; + + private readonly string ACKNOWLEDGE_DUPS_OK = "dups-ok"; + + private readonly string ACKNOWLEDGE_TRANSACTED = "transacted"; + + private readonly string CONCURRENCY_ATTRIBUTE = "concurrency"; + + private readonly string RECOVERY_INTERVAL_ATTRIBUTE = "recovery-interval"; + + private readonly string MAX_RECOVERY_TIME_ATTRIBUTE = "max-recovery-time"; + + private readonly string CONNECTION_FACTORY_ATTRIBUTE = "connection-factory"; + + #endregion + + #region IObjectDefinitionParser Members + + /// + /// Parse the specified XmlElement and register the resulting + /// ObjectDefinitions with the IObjectDefinitionRegistry + /// embedded in the supplied + /// + /// The element to be parsed. + /// TThe object encapsulating the current state of the parsing process. + /// Provides access to a IObjectDefinitionRegistry + /// The primary object definition. + /// + ///

+ /// This method is never invoked if the parser is namespace aware + /// and was called to process the root node. + ///

+ ///
+ public IObjectDefinition ParseElement(XmlElement element, ParserContext parserContext) + { + + XmlNodeList childNodes = element.ChildNodes; + foreach (XmlNode childNode in childNodes) + { + if (childNode.NodeType == XmlNodeType.Element) + { + string localName = childNode.LocalName; + if (LISTENER_ELEMENT.Equals(localName)) + { + ParseListener((XmlElement) childNode, element, parserContext); + } + } + } + return null; + } + + #endregion + + private void ParseListener(XmlElement listenerElement, XmlElement containerElement, ParserContext parserContext) + { + ObjectDefinitionBuilder listenerDefBuilder = + parserContext.ParserHelper.CreateRootObjectDefinitionBuilder(typeof (MessageListenerAdapter)); + + string reference = listenerElement.GetAttribute(REF_ATTRIBUTE); + if (!StringUtils.HasText(reference)) + { + parserContext.ReaderContext.ReportException(listenerElement, LISTENER_ELEMENT, + "Listener '" + REF_ATTRIBUTE + + "' attribute contains empty value."); + } + listenerDefBuilder.AddPropertyValue("HandlerObject", new RuntimeObjectReference(reference)); + + string handlerMethod = null; + if (listenerElement.HasAttribute(METHOD_ATTRIBUTE)) + { + handlerMethod = listenerElement.GetAttribute(METHOD_ATTRIBUTE); + { + if (!StringUtils.HasText(handlerMethod)) + { + parserContext.ReaderContext.ReportException(listenerElement, LISTENER_ELEMENT, + "Listener '" + METHOD_ATTRIBUTE + + "' attribute contains empty value."); + } + } + } + listenerDefBuilder.AddPropertyValue("DefaultHandlerMethod", handlerMethod); + + if (containerElement.HasAttribute(MESSAGE_CONVERTER_ATTRIBUTE)) + { + string messageConverter = containerElement.GetAttribute(MESSAGE_CONVERTER_ATTRIBUTE); + listenerDefBuilder.AddPropertyValue("MessageConverter", new RuntimeObjectReference(messageConverter)); + } + + ObjectDefinitionBuilder containerDefBuilder = ParseContainer(listenerElement, containerElement, parserContext); + + if (listenerElement.HasAttribute(RESPONSE_DESTINATION_ATTRIBUTE)) + { + string responseDestination = listenerElement.GetAttribute(RESPONSE_DESTINATION_ATTRIBUTE); + bool pubSubDomain = IndicatesPubSub(containerDefBuilder.RawObjectDefinition); + listenerDefBuilder.AddPropertyValue(pubSubDomain ? "DefaultResponseTopicName" : "DefaultResponseQueueName", + responseDestination); + if (containerDefBuilder.RawObjectDefinition.PropertyValues.Contains("DestinationResolver")) + { + listenerDefBuilder.AddPropertyValue("DestinationResolver", + containerDefBuilder.RawObjectDefinition.PropertyValues.GetPropertyValue + ( + "DestinationResolver").Value); + } + } + + containerDefBuilder.AddPropertyValue("MessageListener", listenerDefBuilder.ObjectDefinition); + + string containerObjectName = listenerElement.GetAttribute(ID_ATTRIBUTE); + // If no object id is given auto generate one using the ReaderContext's ObjectNameGenerator + if (!StringUtils.HasText(containerObjectName)) + { + containerObjectName = + parserContext.ReaderContext.GenerateObjectName(containerDefBuilder.RawObjectDefinition); + } + + parserContext.Registry.RegisterObjectDefinition(containerObjectName, containerDefBuilder.ObjectDefinition); + } + + private ObjectDefinitionBuilder ParseContainer(XmlElement listenerElement, XmlElement containerElement, + ParserContext parserContext) + { + //Only support SimpleMessageListenerContainer + ObjectDefinitionBuilder containerDef = + parserContext.ParserHelper.CreateRootObjectDefinitionBuilder(typeof (SimpleMessageListenerContainer)); + + ParseListenerConfiguration(listenerElement, parserContext, containerDef); + ParseContainerConfiguration(containerElement, parserContext, containerDef); + + string connectionFactoryObjectName = "connectionFactory"; + if (containerElement.HasAttribute(CONNECTION_FACTORY_ATTRIBUTE)) + { + connectionFactoryObjectName = containerElement.GetAttribute(CONNECTION_FACTORY_ATTRIBUTE); + if (!StringUtils.HasText(connectionFactoryObjectName)) + { + parserContext.ReaderContext.ReportException(listenerElement, LISTENER_ELEMENT, + "Listener container '" + CONNECTION_FACTORY_ATTRIBUTE + + "' attribute contains empty value."); + } + } + + containerDef.AddPropertyValue("ConnectionFactory", new RuntimeObjectReference(connectionFactoryObjectName)); + + string destinationResolverBeanName = containerElement.GetAttribute(DESTINATION_RESOLVER_ATTRIBUTE); + if (StringUtils.HasText(destinationResolverBeanName)) + { + containerDef.AddPropertyValue("DestinationResolver", + new RuntimeObjectReference(destinationResolverBeanName)); + } + + string acknowledge = containerElement.GetAttribute(ACKNOWLEDGE_ATTRIBUTE); + if (StringUtils.HasText(acknowledge)) + { + int acknowledgementMode = ParseAcknowledgementMode(containerElement, parserContext); + containerDef.AddPropertyValue("SessionAcknowledgeMode", acknowledgementMode); + } + + int[] concurrency = ParseConcurrency(containerElement, parserContext); + if (concurrency != null) + { + containerDef.AddPropertyValue("ConcurrentConsumers", concurrency[1]); + } + containerDef.AddPropertyValue("RecoveryInterval", ParseRecoveryInterval(containerElement, parserContext)); + + containerDef.AddPropertyValue("MaxRecoveryTime", ParseMaxRecoveryTime(containerElement, parserContext)); + + return containerDef; + } + + private bool IndicatesPubSub(AbstractObjectDefinition configDef) + { + return (bool) configDef.PropertyValues.GetPropertyValue("PubSubDomain").Value; + } + + private void ParseListenerConfiguration(XmlElement ele, ParserContext parserContext, + ObjectDefinitionBuilder containerDef) + { + string destination = ele.GetAttribute(DESTINATION_ATTRIBUTE); + if (!StringUtils.HasText(destination)) + { + parserContext.ReaderContext.ReportException(ele, LISTENER_ELEMENT, + "Listener '" + DESTINATION_ATTRIBUTE + + "' attribute contains empty value."); + } + containerDef.AddPropertyValue("DestinationName", destination); + + if (ele.HasAttribute(SUBSCRIPTION_ATTRIBUTE)) + { + string subscription = ele.GetAttribute(SUBSCRIPTION_ATTRIBUTE); + if (!StringUtils.HasText(subscription)) + { + parserContext.ReaderContext.ReportException(ele, SUBSCRIPTION_ATTRIBUTE, + "Listener '" + SUBSCRIPTION_ATTRIBUTE + + "' attribute contains empty value."); + } + containerDef.AddPropertyValue("DurableSubscriptionName", subscription); + } + + if (ele.HasAttribute(SELECTOR_ATTRIBUTE)) + { + string selector = ele.GetAttribute(SELECTOR_ATTRIBUTE); + if (!StringUtils.HasText(selector)) + { + parserContext.ReaderContext.ReportException(ele, selector, + "Listener '" + SELECTOR_ATTRIBUTE + + "' attribute contains empty value."); + } + containerDef.AddPropertyValue("MessageSelector", selector); + } + } + + private void ParseContainerConfiguration(XmlElement ele, ParserContext parserContext, + ObjectDefinitionBuilder containerDef) + { + string destinationType = ele.GetAttribute(DESTINATION_TYPE_ATTRIBUTE); + bool pubSubDomain = false; + bool subscriptionDurable = false; + if (DESTINATION_TYPE_DURABLE_TOPIC.Equals(destinationType)) + { + pubSubDomain = true; + subscriptionDurable = true; + } + else if (DESTINATION_TYPE_TOPIC.Equals(destinationType)) + { + pubSubDomain = true; + } + else if ("".Equals(destinationType) || DESTINATION_TYPE_QUEUE.Equals(destinationType)) + { + // the default: queue + } + else + { + parserContext.ReaderContext.ReportException(ele, destinationType, + "Invalid listener container '" + DESTINATION_TYPE_ATTRIBUTE + + "': only 'queue', 'topic' and 'durableTopic' supported"); + } + + containerDef.AddPropertyValue("PubSubDomain", pubSubDomain); + containerDef.AddPropertyValue("SubscriptionDurable", subscriptionDurable); + + if (ele.HasAttribute(CLIENT_ID_ATTRIBUTE)) + { + string clientId = ele.GetAttribute(CLIENT_ID_ATTRIBUTE); + if (!StringUtils.HasText(clientId)) + { + parserContext.ReaderContext.ReportException(ele, clientId, + "Listener '" + CLIENT_ID_ATTRIBUTE + + "' attribute contains empty value."); + } + containerDef.AddPropertyValue("ClientId", clientId); + } + } + + private int ParseAcknowledgementMode(XmlElement element, ParserContext parserContext) + { + string acknowledge = element.GetAttribute(ACKNOWLEDGE_ATTRIBUTE); + if (acknowledge.Equals(ACKNOWLEDGE_TRANSACTED)) + { + return Session.SESSION_TRANSACTED; + } + else if (acknowledge.Equals(ACKNOWLEDGE_DUPS_OK)) + { + return Session.DUPS_OK_ACKNOWLEDGE; + } + else if (acknowledge.Equals(ACKNOWLEDGE_CLIENT)) + { + return Session.CLIENT_ACKNOWLEDGE; + } + //TODO other ack modes. + else if (!acknowledge.Equals(ACKNOWLEDGE_AUTO)) + { + parserContext.ReaderContext.ReportException(element, ACKNOWLEDGE_ATTRIBUTE, + "Invalid listener container 'acknowledge' setting ['" + + acknowledge + + "]: only \"auto\", \"client\", \"dups-ok\" and \"transacted\" supported."); + } + return Session.AUTO_ACKNOWLEDGE; + } + + private int[] ParseConcurrency(XmlElement ele, ParserContext parserContext) + { + string concurrency = ele.GetAttribute(CONCURRENCY_ATTRIBUTE); + if (!StringUtils.HasText(concurrency)) + { + return null; + } + try + { + return new int[] {1, Int32.Parse(concurrency)}; + } + catch (FormatException ex) + { + parserContext.ReaderContext.ReportException(ele, CONCURRENCY_ATTRIBUTE, + "Invalid concurrency value [" + concurrency + "]: only " + + "integer (e.g. \"5\") values upported.", ex); + return null; + } + } + + 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) + { + string recoverTime = ele.GetAttribute(MAX_RECOVERY_TIME_ATTRIBUTE); + if (!StringUtils.HasText(recoverTime)) + { + return SimpleMessageListenerContainer.DEFAULT_MAX_RECOVERY_TIME; + } + try + { + 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; + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..7078c0dc --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Config/spring-ems-1.2.xsd @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsGatewaySupport.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsGatewaySupport.cs new file mode 100644 index 00000000..e87ca79d --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsGatewaySupport.cs @@ -0,0 +1,119 @@ +#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 Common.Logging; +using Spring.Objects.Factory; +using TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// + /// Convenient super class for application classes that need EMS access. + /// + /// + /// Requires a ConnectionFactory or a EmsTemplate instance to be set. + /// It will create its own EmsTemplate if a ConnectionFactory is passed in. + /// A custom EmsTemplate instance can be created for a given ConnectionFactory + /// through overriding the createEmsTemplate method. + /// + /// + public class EmsGatewaySupport : IInitializingObject + { + + #region Logging + + private readonly ILog logger = LogManager.GetLogger(typeof(EmsGatewaySupport)); + + #endregion + + private EmsTemplate jmsTemplate; + + + /// + /// Gets or sets the EMS template for the gateway. + /// + /// The EMS template. + public EmsTemplate EmsTemplate + { + get { return jmsTemplate; } + set { jmsTemplate = value; } + } + + /// + /// Gets or sets he EMS connection factory to be used by the gateway. + /// Will automatically create a EmsTemplate for the given ConnectionFactory. + /// + /// The connection factory. + public ConnectionFactory ConnectionFactory + { + get + { + return (jmsTemplate != null ? this.jmsTemplate.ConnectionFactory : null); + } + set + { + this.jmsTemplate = CreateEmsTemplate(value); + } + } + + /// + /// Creates a EmsTemplate for the given ConnectionFactory. + /// + /// Only invoked if populating the gateway with a ConnectionFactory reference. + /// Can be overridden in subclasses to provide a different EmsTemplate instance + /// + /// + /// The connection factory. + /// + protected virtual EmsTemplate CreateEmsTemplate(ConnectionFactory connectionFactory) + { + return new EmsTemplate(connectionFactory); + } + + /// + /// Ensures that the JmsTemplate is specified and calls . + /// + public virtual void AfterPropertiesSet() + { + if (jmsTemplate == null) + { + throw new ArgumentException("connectionFactory or jmsTemplate is required"); + } + try + { + InitGateway(); + } + catch (Exception e) + { + throw new ObjectInitializationException("Initialization of the EMS gateway failed: " + e.Message, e); + } + } + + /// + /// Subclasses can override this for custom initialization behavior. + /// Gets called after population of this instance's properties. + /// + protected virtual void InitGateway() + { + + } + } +} diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsTemplate.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsTemplate.cs new file mode 100644 index 00000000..c2182ad8 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/EmsTemplate.cs @@ -0,0 +1,1649 @@ +#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 Common.Logging; +using Spring.Messaging.Ems.Connections; +using Spring.Messaging.Ems.Support; +using Spring.Messaging.Ems.Support.Converter; +using Spring.Messaging.Ems.Support.Destinations; +using Spring.Transaction.Support; +using Spring.Util; +using TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// Helper class that simplifies EMS access code. + /// + /// If you want to use dynamic destination creation, you must specify + /// the type of EMS destination to create, using the "pubSubDomain" property. + /// For other operations, this is not necessary. + /// Point-to-Point (Queues) is the default domain. + /// + /// Default settings for EMS Sessions is "auto-acknowledge". + /// + /// This template uses a DynamicDestinationResolver and a SimpleMessageConverter + /// as default strategies for resolving a destination name or converting a message, + /// respectively. + /// + /// + /// Mark Pollack + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class EmsTemplate : EmsDestinationAccessor, IEmsOperations + { + #region Logging + + private readonly ILog logger = LogManager.GetLogger(typeof(EmsTemplate)); + + + #endregion + #region Fields + + /// + /// Timeout value indicating that a receive operation should + /// check if a message is immediately available without blocking. + /// + public static readonly long DEFAULT_RECEIVE_TIMEOUT = -1; + + private EmsTemplateResourceFactory transactionalResourceFactory; + + private object defaultDestination; + + private IMessageConverter messageConverter; + + + private bool messageIdEnabled = true; + + private bool messageTimestampEnabled = true; + + private bool pubSubNoLocal = false; + + private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT; + + private bool explicitQosEnabled = false; + + private int priority = Message.DEFAULT_PRIORITY; + + private int deliveryMode = Message.DEFAULT_DELIVERY_MODE; + + private long timeToLive = Message.DEFAULT_TIME_TO_LIVE; + + private EmsResources emsResources = new EmsResources(); + + private bool cacheEmsResources = true; + + + #endregion + + #region Constructor (s) + + /// Create a new EmsTemplate. + /// + /// Note: The ConnectionFactory has to be set before using the instance. + /// This constructor can be used to prepare a EmsTemplate via an ObjectFactory, + /// typically setting the ConnectionFactory. + /// + public EmsTemplate() + { + transactionalResourceFactory = new EmsTemplateResourceFactory(this); + InitDefaultStrategies(); + } + + + /// Create a new EmsTemplate, given a ConnectionFactory. + /// the ConnectionFactory to obtain Connections from + /// + public EmsTemplate(ConnectionFactory connectionFactory) + : this() + { + ConnectionFactory = connectionFactory; + AfterPropertiesSet(); + } + + #endregion + + #region Methods + + /// Initialize the default implementations for the template's strategies: + /// DynamicDestinationResolver and SimpleMessageConverter. + /// + protected virtual void InitDefaultStrategies() + { + MessageConverter = new SimpleMessageConverter(); + } + + private void CheckDefaultDestination() + { + if (defaultDestination == null) + { + throw new SystemException( + "No defaultDestination or defaultDestinationName specified. Check configuration of EmsTemplate."); + } + } + + + private void CheckMessageConverter() + { + if (MessageConverter == null) + { + throw new SystemException("No messageConverter registered. Check configuration of EmsTemplate."); + } + } + + /// Execute the action specified by the given action object within a + /// EMS Session. + /// + /// Generalized version of execute(SessionCallback), + /// allowing the EMS Connection to be started on the fly. + ///

Use execute(SessionCallback) for the general case. + /// Starting the EMS Connection is just necessary for receiving messages, + /// which is preferably achieved through the receive methods.

+ ///
+ /// callback object that exposes the session + /// + /// Start the connection before performing callback action. + /// + /// the result object from working with the session + /// + /// EMSException if there is any problem + public virtual object Execute(ISessionCallback action, bool startConnection) + { + AssertUtils.ArgumentNotNull(action, "Callback object must not be null"); + + Connection con = null; + Session session = null; + bool sessionInTLS = true; + + //NOTE: Not closing session or connection unless session is not returned from + // ConnectionFactoryUtils.DoGetTransactionalSession and CacheEmsResources is set to false + try + { + Session sessionToUse = + ConnectionFactoryUtils.DoGetTransactionalSession(ConnectionFactory, transactionalResourceFactory, + startConnection); + if (sessionToUse == null) + { + sessionInTLS = false; + con = CreateConnection(); + session = CreateSession(con); + if (startConnection) + { + con.Start(); + } + sessionToUse = session; + } + if (logger.IsDebugEnabled) + { + logger.Debug("Executing callback on EMS Session [" + sessionToUse + "]"); + } + return action.DoInEms(sessionToUse); + } + finally + { + if (!sessionInTLS && !CacheEmsResources) + { + EmsUtils.CloseSession(session); + ConnectionFactoryUtils.ReleaseConnection(con, ConnectionFactory, startConnection); + } + } + } + + #endregion + + #region Properties + + /// + /// Gets or sets the default destination to be used on send/receive operations that do not + /// have a destination parameter. + /// + /// Alternatively, specify a "defaultDestinationName", to be + /// dynamically resolved via the DestinationResolver. + /// The default destination. + virtual public Destination DefaultDestination + { + get { return (defaultDestination as Destination); } + + set { defaultDestination = value; } + } + + + /// + /// Gets or sets the name of the default destination name + /// to be used on send/receive operations that + /// do not have a destination parameter. + /// + /// + /// Alternatively, specify a EMS Destination object as "DefaultDestination" + /// + /// The name of the default destination. + virtual public string DefaultDestinationName + { + get { return (defaultDestination as string); } + + set { defaultDestination = value; } + } + + /// + /// Gets or sets the message converter for this template. + /// + /// + /// Used to resolve + /// Object parameters to convertAndSend methods and Object results + /// from receiveAndConvert methods. + ///

The default converter is a SimpleMessageConverter, which is able + /// to handle BytesMessages, TextMessages and ObjectMessages.

+ ///
+ /// The message converter. + virtual public IMessageConverter MessageConverter + { + get { return messageConverter; } + + set { messageConverter = value; } + } + + /// + /// Gets or sets a value indicating whether Message Ids are enabled. + /// + /// true if message id enabled; otherwise, false. + virtual public bool MessageIdEnabled + { + get { return messageIdEnabled; } + + set { messageIdEnabled = value; } + } + + /// + /// Gets or sets a value indicating whether message timestamps are enabled. + /// + /// + /// true if [message timestamp enabled]; otherwise, false. + /// + virtual public bool MessageTimestampEnabled + { + get { return messageTimestampEnabled; } + + set { messageTimestampEnabled = value; } + } + + + /// + /// Gets or sets a value indicating whether to inhibit the delivery of messages published by its own connection. + /// + /// + /// true if inhibit the delivery of messages published by its own connection; otherwise, false. + virtual public bool PubSubNoLocal + { + get { return pubSubNoLocal; } + + set { pubSubNoLocal = value; } + } + + /// + /// Gets or sets the receive timeout to use for recieve calls. + /// + /// The default is -1, which means no timeout. + /// The receive timeout. + virtual public long ReceiveTimeout + { + get { return receiveTimeout; } + + set { receiveTimeout = value; } + } + + /// + /// Gets or sets a value indicating whether to use explicit Quality of Service values. + /// + /// If "true", then the values of deliveryMode, priority, and timeToLive + /// will be used when sending a message. Otherwise, the default values, + /// that may be set administratively, will be used + /// true if use explicit QoS values; otherwise, false. + virtual public bool ExplicitQosEnabled + { + get { return explicitQosEnabled; } + + set { explicitQosEnabled = value; } + } + + /// + /// Sets a value indicating the delivery mode QOS + /// + /// + /// This will set the delivery to persistent or non-persistent + /// Default value is Message.DEFAULT_DELIVERY_MODE + /// + /// true if [delivery persistent]; otherwise, false. + virtual public int DeliveryMode + { + get { return deliveryMode; } + + set { deliveryMode = value; } + } + + /// + /// Gets or sets the priority when sending. + /// + /// Since a default value may be defined administratively, + /// this is only used when "isExplicitQosEnabled" equals "true". + /// The priority. + virtual public int Priority + { + get { return priority; } + + set { priority = value; } + } + + /// + /// Gets or sets the time to live when sending + /// + /// Since a default value may be defined administratively, + /// this is only used when "isExplicitQosEnabled" equals "true". + /// The time to live. + virtual public long TimeToLive + { + get { return timeToLive; } + + set { timeToLive = value; } + } + + /// + /// Gets or sets a value indicating whether the EmsTemplate should itself + /// be responsible for caching EMS Connection/Session/MessageProducer as compared to + /// creating new instances per operation (unless such resources are already + /// present in Thread-Local storage either due to the use of EmsTransactionMananger or + /// SimpleMessageListenerContainer at an outer calling layer. + /// + /// Connection/Session/MessageProducer are thread-safe classes in TIBCO EMS. + /// true to locally cache ems resources; otherwise, false. + virtual public bool CacheEmsResources + { + get { return cacheEmsResources; } + set { cacheEmsResources = value; } + } + + + #endregion + + /// + /// Extract the content from the given JMS message. + /// + /// The Message to convert (can be null). + /// The content of the message, or null if none + protected virtual object DoConvertFromMessage(Message message) + { + if (message != null) + { + return MessageConverter.FromMessage(message); + } + return null; + } + + #region EMS Factory Methods + + /// Fetch an appropriate Connection from the given EmsResourceHolder. + /// + /// the EmsResourceHolder + /// + /// an appropriate Connection fetched from the holder, + /// or null if none found + /// + protected virtual Connection GetConnection(EmsResourceHolder holder) + { + return holder.GetConnection(); + } + + /// Fetch an appropriate Session from the given EmsResourceHolder. + /// + /// the EmsResourceHolder + /// + /// an appropriate Session fetched from the holder, + /// or null if none found + /// + protected virtual Session GetSession(EmsResourceHolder holder) + { + return holder.GetSession(); + } + + /// Create a EMS MessageProducer for the given Session and Destination, + /// configuring it to disable message ids and/or timestamps (if necessary). + ///

Delegates to doCreateProducer for creation of the raw + /// EMS MessageProducer

+ ///
+ /// the EMS Session to create a MessageProducer for + /// + /// the EMS Destination to create a MessageProducer for + /// + /// the new EMS MessageProducer + /// + /// EMSException if thrown by EMS API methods + /// + /// + /// + /// + /// + /// + protected virtual MessageProducer CreateProducer(Session session, Destination destination) + { + MessageProducer producer = DoCreateProducer(session, destination); + if (!MessageIdEnabled) + { + producer.DisableMessageID = true; + } + if (!MessageTimestampEnabled) + { + producer.DisableMessageTimestamp = true; + } + return producer; + } + + + /// + /// Determines whether the given Session is locally transacted, that is, whether + /// its transaction is managed by this template class's Session handling + /// and not by an external transaction coordinator. + /// + /// + /// The Session's own transacted flag will already have been checked + /// before. This method is about finding out whether the Session's transaction + /// is local or externally coordinated. + /// + /// The session to check. + /// + /// true if the session is locally transacted; otherwise, false. + /// + protected virtual bool IsSessionLocallyTransacted(Session session) + { + return SessionTransacted && + !ConnectionFactoryUtils.IsSessionTransactional(session, ConnectionFactory); + } + + /// Create a raw EMS MessageProducer for the given Session and Destination. + /// + /// If CacheJmsResource is true, then the producer + /// will be created upon the first invocation and will retrun the same + /// producer (per destination) on all subsequent calls. + /// + /// the EMS Session to create a MessageProducer for + /// + /// the EMS Destination to create a MessageProducer for + /// + /// the new EMS MessageProducer + /// + /// EMSException if thrown by EMS API methods + protected virtual MessageProducer DoCreateProducer(Session session, Destination destination) + { + if (CacheEmsResources) + { + if (destination == null) + { + if (emsResources.UnspecifiedDestinationMessageProducer == null) + { + emsResources.UnspecifiedDestinationMessageProducer = session.CreateProducer(destination); + } + return emsResources.UnspecifiedDestinationMessageProducer; + } + MessageProducer producer = (MessageProducer)emsResources.Producers[destination]; + if (producer != null) + { + #region Logging + + if (logger.IsDebugEnabled) + { + logger.Debug("Found cached MessageProducer for destination [" + destination + "]"); + } + + #endregion + } + else + { + producer = session.CreateProducer(destination); + emsResources.Producers.Add(destination, producer); + #region Logging + + if (logger.IsDebugEnabled) + { + logger.Debug("Created cached MessageProducer for destination [" + destination + "]"); + } + + #endregion + } + return producer; + } + else + { + return session.CreateProducer(destination); + } + } + + /// Create a EMS MessageConsumer for the given Session and Destination. + /// + /// the EMS Session to create a MessageConsumer for + /// + /// the EMS Destination to create a MessageConsumer for + /// + /// the message selector for this consumer (can be null) + /// + /// the new EMS MessageConsumer + /// + /// EMSException if thrown by EMS API methods + protected virtual MessageConsumer CreateConsumer(Session session, Destination destination, + string messageSelector) + { + // Only pass in the NoLocal flag in case of a Topic: + // Some EMS providers, such as WebSphere MQ 6.0, throw IllegalStateException + // in case of the NoLocal flag being specified for a Queue. + if (PubSubDomain) + { + return session.CreateConsumer(destination, messageSelector, PubSubNoLocal); + } + else + { + return session.CreateConsumer(destination, messageSelector); + } + } + + /// Create a EMS Connection via this template's ConnectionFactory. + /// + /// If CacheJmsResource is true, then the connection + /// will be created upon the first invocation and will retrun the same + /// connection on all subsequent calls. + /// + /// A EMS Connection + /// + /// EMSException if thrown by EMS API methods + protected override Connection CreateConnection() + { + if (CacheEmsResources) + { + if (emsResources.Connection == null) + { + emsResources.Connection = ConnectionFactory.CreateConnection(); + } + return emsResources.Connection; + + } + else + { + return ConnectionFactory.CreateConnection(); + } + } + + /// Create a EMS Session for the given Connection. + /// + /// If CacheJmsResource is true, then the session + /// will be created upon the first invocation and will retrun the same + /// session on all subsequent calls. + /// + /// the EMS Connection to create a Session for + /// + /// the new EMS Session + /// + /// EMSException if thrown by EMS API methods + protected override Session CreateSession(Connection con) + { + if (CacheEmsResources) + { + if (emsResources.Session == null) + { + emsResources.Session = emsResources.Connection.CreateSession(SessionTransacted, SessionAcknowledgeMode); + } + return emsResources.Session; + } + else + { + return con.CreateSession(SessionTransacted, SessionAcknowledgeMode); + } + } + + /// + /// Send the given message. + /// + /// The session to operate on. + /// The destination to send to. + /// The message creator delegate callback to create a Message. + protected internal virtual void DoSend(Session session, Destination destination, MessageCreatorDelegate messageCreatorDelegate) + { + AssertUtils.ArgumentNotNull(messageCreatorDelegate, "IMessageCreatorDelegate must not be null"); + DoSend(session, destination, null, messageCreatorDelegate); + } + + /// + /// Send the given message. + /// + /// The session to operate on. + /// The destination to send to. + /// The message creator callback to create a Message. + protected internal virtual void DoSend(Session session, Destination destination, IMessageCreator messageCreator) + { + AssertUtils.ArgumentNotNull(messageCreator, "IMessageCreator must not be null"); + DoSend(session, destination, messageCreator, null); + } + + /// Send the given EMS message. + /// the EMS Session to operate on + /// + /// the EMS Destination to send to + /// + /// callback to create a EMS Message + /// + /// delegate callback to create a EMS Message + /// + /// EMSException if thrown by EMS API methods + protected internal virtual void DoSend(Session session, Destination destination, IMessageCreator messageCreator, + MessageCreatorDelegate messageCreatorDelegate) + { + + + MessageProducer producer = CreateProducer(session, destination); + try + { + + Message message = null; + if (messageCreator != null) + { + message = messageCreator.CreateMessage(session) ; + } + else { + message = messageCreatorDelegate(session); + } + if (logger.IsDebugEnabled) + { + logger.Debug("Sending created message [" + message + "]"); + } + DoSend(producer, message); + + // Check commit, avoid commit call is Session transaction is externally coordinated. + if (session.Transacted && IsSessionLocallyTransacted(session)) + { + // Transacted session created by this template -> commit. + EmsUtils.CommitIfNecessary(session); + } + } + finally + { + EmsUtils.CloseMessageProducer(producer); + } + } + + + /// Actually send the given EMS message. + /// the EMS MessageProducer to send with + /// + /// the EMS Message to send + /// + /// EMSException if thrown by EMS API methods + protected virtual void DoSend(MessageProducer producer, Message message) + { + if (ExplicitQosEnabled) + { + producer.Send(message, DeliveryMode, Priority, TimeToLive); + } + else + { + producer.Send(message); + } + } + + + #endregion + + #region IEmsOperations Implementation + + /// + /// Execute the action specified by the given action object within + /// a EMS Session. + /// + /// delegate that exposes the session + /// + /// the result object from working with the session + /// + /// + /// Note that the value of PubSubDomain affects the behavior of this method. + /// If PubSubDomain equals true, then a Session is passed to the callback. + /// If false, then a Session is passed to the callback.b + /// + /// EMSException if there is any problem + public object Execute(SessionDelegate del) + { + return Execute(new ExecuteSessionCallbackUsingDelegate(del)); + } + + /// Execute the action specified by the given action object within + /// a EMS Session. + ///

Note: The value of PubSubDomain affects the behavior of this method. + /// If PubSubDomain equals true, then a Session is passed to the callback. + /// If false, then a Session is passed to the callback.

+ ///
+ /// callback object that exposes the session + /// + /// the result object from working with the session + /// + /// EMSException if there is any problem + public object Execute(ISessionCallback action) + { + return Execute(action, false); + } + + /// Send a message to a EMS destination. The callback gives access to + /// the EMS session and MessageProducer in order to do more complex + /// send operations. + /// + /// callback object that exposes the session/producer pair + /// + /// the result object from working with the session + /// + /// EMSException if there is any problem + public object Execute(IProducerCallback action) + { + return Execute(new ProducerCreatorCallback(this, action)); + } + + /// Send a message to a EMS destination. The callback gives access to + /// the EMS session and MessageProducer in order to do more complex + /// send operations. + /// + /// delegate that exposes the session/producer pair + /// + /// the result object from working with the session + /// + /// EMSException if there is any problem + public object Execute(ProducerDelegate del) + { + return Execute(new ProducerCreatorCallback(this, del)); + } + + /// Send a message to the default destination. + ///

This will only work with a default destination specified!

+ ///
+ /// delegate callback to create a message + /// + /// EMSException if there is any problem + public void SendWithDelegate(MessageCreatorDelegate messageCreatorDelegate) + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + SendWithDelegate(DefaultDestination, messageCreatorDelegate); + } + else + { + SendWithDelegate(DefaultDestinationName, messageCreatorDelegate); + } + } + + /// Send a message to the specified destination. + /// The MessageCreator callback creates the message given a Session. + /// + /// the destination to send this message to + /// + /// delegate callback to create a message + /// + /// EMSException if there is any problem + public void SendWithDelegate(Destination destination, MessageCreatorDelegate messageCreatorDelegate) + { + Execute(new SendDestinationCallback(this, destination, messageCreatorDelegate), false); + } + + /// Send a message to the specified destination. + /// The MessageCreator callback creates the message given a Session. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// delegate callback to create a message + /// + /// EMSException if there is any problem + public void SendWithDelegate(string destinationName, MessageCreatorDelegate messageCreatorDelegate) + { + Execute(new SendDestinationCallback(this, destinationName, messageCreatorDelegate), false); + } + + /// Send a message to the default destination. + ///

This will only work with a default destination specified!

+ ///
+ /// callback to create a message + /// + /// EMSException if there is any problem + public void Send(IMessageCreator messageCreator) + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + Send(DefaultDestination, messageCreator); + } + else + { + Send(DefaultDestinationName, messageCreator); + } + } + + /// Send a message to the specified destination. + /// The MessageCreator callback creates the message given a Session. + /// + /// the destination to send this message to + /// + /// callback to create a message + /// + /// EMSException if there is any problem + public void Send(Destination destination, IMessageCreator messageCreator) + { + + Execute(new SendDestinationCallback(this, destination, messageCreator), false); + } + + /// Send a message to the specified destination. + /// The MessageCreator callback creates the message given a Session. + /// + /// the destination to send this message to + /// + /// callback to create a message + /// + /// EMSException if there is any problem + public void Send(string destinationName, IMessageCreator messageCreator) + { + Execute(new SendDestinationCallback(this, destinationName, messageCreator), false); + } + /// Send the given object to the default destination, converting the object + /// to a EMS message with a configured IMessageConverter. + ///

This will only work with a default destination specified!

+ ///
+ /// the object to convert to a message + /// + /// EMSException if there is any problem + public void ConvertAndSend(object message) + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + ConvertAndSend(DefaultDestination, message); + } + else + { + ConvertAndSend(DefaultDestinationName, message); + } + } + + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. + /// + /// the destination to send this message to + /// + /// the object to convert to a message + /// + /// EMSException if there is any problem + public void ConvertAndSend(Destination destination, object message) + { + CheckMessageConverter(); + Send(destination, new SimpleMessageCreator(this, message)); + } + + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the object to convert to a message + /// + /// EMSException if there is any problem + public void ConvertAndSend(string destinationName, object message) + { + Send(destinationName, new SimpleMessageCreator(this, message)); + } + + /// Send the given object to the default destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + ///

This will only work with a default destination specified!

+ ///
+ /// the object to convert to a message + /// + /// the callback to modify the message + /// + /// EMSException if there is any problem + public void ConvertAndSend(object message, IMessagePostProcessor postProcessor) + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + ConvertAndSend(DefaultDestination, message, postProcessor); + } + else + { + ConvertAndSend(DefaultDestinationName, message, postProcessor); + } + } + + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the destination to send this message to + /// + /// the object to convert to a message + /// + /// the callback to modify the message + /// + /// EMSException if there is any problem + public void ConvertAndSend(Destination destination, object message, IMessagePostProcessor postProcessor) + { + CheckMessageConverter(); + Send(destination, new ConvertAndSendMessageCreator(this, message, postProcessor)); + + } + + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the object to convert to a message. + /// + /// the callback to modify the message + /// + /// EMSException if there is any problem + public void ConvertAndSend(string destinationName, object message, IMessagePostProcessor postProcessor) + { + CheckMessageConverter(); + Send(destinationName, new ConvertAndSendMessageCreator(this, message, postProcessor)); + } + /// + /// Send the given object to the default destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + ///

This will only work with a default destination specified!

+ ///
+ /// the object to convert to a message + /// the callback to modify the message + /// EMSException if there is any problem + public void ConvertAndSendWithDelegate(object message, MessagePostProcessorDelegate postProcessor) + { + //Execute(new SendDestinationCallback(this, destination, messageCreatorDelegate), false); + CheckDefaultDestination(); + if (DefaultDestination != null) + { + ConvertAndSendWithDelegate(DefaultDestination, message, postProcessor); + } + else + { + ConvertAndSendWithDelegate(DefaultDestinationName, message, postProcessor); + } + } + + /// + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the destination to send this message to + /// the object to convert to a message + /// the callback to modify the message + /// EMSException if there is any problem + public void ConvertAndSendWithDelegate(Destination destination, object message, + MessagePostProcessorDelegate postProcessor) + { + CheckMessageConverter(); + Send(destination, new ConvertAndSendMessageCreator(this, message, postProcessor)); + } + + /// + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// the object to convert to a message. + /// the callback to modify the message + /// EMSException if there is any problem + public void ConvertAndSendWithDelegate(string destinationName, object message, + MessagePostProcessorDelegate postProcessor) + { + CheckMessageConverter(); + Send(destinationName, new ConvertAndSendMessageCreator(this, message, postProcessor)); + + } + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///

This will only work with a default destination specified!

+ ///
+ /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + public Message Receive() + { + CheckDefaultDestination(); + if (DefaultDestination != null) + { + return Receive(DefaultDestination); + } + else + { + return Receive(DefaultDestinationName); + } + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the destination to receive a message from + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + public Message Receive(Destination destination) + { + return Execute(new ReceiveCallback(this, destination)) as Message; + } + + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + public Message Receive(string destinationName) + { + return Execute(new ReceiveCallback(this, destinationName)) as Message; + } + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///

This will only work with a default destination specified!

+ ///
+ /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + public Message ReceiveSelected(string messageSelector) + { + CheckDefaultDestination(); + if (DefaultDestination!= null) + { + return ReceiveSelected(DefaultDestination, messageSelector); + } + else + { + return ReceiveSelected(DefaultDestinationName, messageSelector); + } + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the destination to receive a message from + /// + /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + public Message ReceiveSelected(Destination destination, string messageSelector) + { + return Execute(new ReceiveSelectedCallback(this, destination, messageSelector), true) as Message; + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + public Message ReceiveSelected(string destinationName, string messageSelector) + { + return Execute(new ReceiveSelectedCallback(this, destinationName, messageSelector), true) as Message; + + } + + /// + /// Receive a message. + /// + /// The session to operate on. + /// The destination to receive from. + /// The message selector for this consumer (can be null + /// The Message received, or null if none. + protected virtual Message DoReceive(Session session, Destination destination, string messageSelector) + { + return DoReceive(session, CreateConsumer(session, destination, messageSelector)); + } + + /// + /// Receive a message. + /// + /// The session to operate on. + /// The consumer to receive with. + /// The Message received, or null if none + protected virtual Message DoReceive(Session session, MessageConsumer consumer) + { + try + { + long timeout = ReceiveTimeout; + EmsResourceHolder resourceHolder = + (EmsResourceHolder)TransactionSynchronizationManager.GetResource(ConnectionFactory); + if (resourceHolder != null && resourceHolder.HasTimeout) + { + timeout = Convert.ToInt64(resourceHolder.TimeToLiveInMilliseconds); + } + Message message = (timeout > 0) + ? consumer.Receive(timeout) + : consumer.Receive(); + if (session.Transacted) + { + // Commit necessary - but avoid commit call is Session transaction is externally coordinated. + if (IsSessionLocallyTransacted(session)) + { + // Transacted session created by this template -> commit. + EmsUtils.CommitIfNecessary(session); + } + } + else if (IsClientAcknowledge(session)) + { + // Manually acknowledge message, if any. + if (message != null) + { + message.Acknowledge(); + } + } + return message; + } + finally + { + EmsUtils.CloseMessageConsumer(consumer); + } + } + + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///

This will only work with a default destination specified!

+ ///
+ /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + public object ReceiveAndConvert() + { + CheckMessageConverter(); + return DoConvertFromMessage(Receive()); + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the destination to receive a message from + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + public object ReceiveAndConvert(Destination destination) + { + CheckMessageConverter(); + return DoConvertFromMessage(Receive(destination)); + } + + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + public object ReceiveAndConvert(string destinationName) + { + CheckMessageConverter(); + return DoConvertFromMessage(Receive(destinationName)); + } + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///

This will only work with a default destination specified!

+ ///
+ /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + public object ReceiveSelectedAndConvert(string messageSelector) + { + CheckMessageConverter(); + return DoConvertFromMessage(ReceiveSelected(messageSelector)); ; + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the destination to receive a message from + /// + /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + public object ReceiveSelectedAndConvert(Destination destination, string messageSelector) + { + CheckMessageConverter(); + return DoConvertFromMessage(ReceiveSelected(destination, messageSelector)); + } + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + public object ReceiveSelectedAndConvert(string destinationName, string messageSelector) + { + CheckMessageConverter(); + return DoConvertFromMessage(ReceiveSelected(destinationName, messageSelector)); + } + + #endregion + + #region Supporting Internal Classes + + /// + /// ResourceFactory implementation that delegates to this template's callback methods. + /// + private class EmsTemplateResourceFactory : ConnectionFactoryUtils.ResourceFactory + { + private EmsTemplate enclosingTemplateInstance; + + public EmsTemplateResourceFactory(EmsTemplate enclosingInstance) + { + InitBlock(enclosingInstance); + } + + private void InitBlock(EmsTemplate enclosingInstance) + { + enclosingTemplateInstance = enclosingInstance; + } + + public EmsTemplate EnclosingInstance + { + get { return enclosingTemplateInstance; } + } + + public virtual Connection GetConnection(EmsResourceHolder holder) + { + return EnclosingInstance.GetConnection(holder); + } + + public virtual Session GetSession(EmsResourceHolder holder) + { + return EnclosingInstance.GetSession(holder); + } + + public virtual Connection CreateConnection() + { + return EnclosingInstance.CreateConnection(); + } + + public virtual Session CreateSession(Connection con) + { + return EnclosingInstance.CreateSession(con); + } + + public bool SynchedLocalTransactionAllowed + { + get { return EnclosingInstance.SessionTransacted; } + } + } + + private class ProducerCreatorCallback : ISessionCallback + { + private EmsTemplate jmsTemplate; + private IProducerCallback producerCallback; + private ProducerDelegate producerDelegate; + + public ProducerCreatorCallback(EmsTemplate jmsTemplate, IProducerCallback producerCallback) + { + this.jmsTemplate = jmsTemplate; + this.producerCallback = producerCallback; + } + + public ProducerCreatorCallback(EmsTemplate jmsTemplate, ProducerDelegate producerDelegate) + { + this.jmsTemplate = jmsTemplate; + this.producerDelegate = producerDelegate; + } + + + public object DoInEms(Session session) + { + MessageProducer producer = jmsTemplate.CreateProducer(session, null); + try + { + if (producerCallback != null) + { + return producerCallback.DoInEms(session, producer); + } + else + { + return producerDelegate(session, producer); + } + } + finally + { + EmsUtils.CloseMessageProducer(producer); + } + + } + } + + private class ReceiveCallback : ISessionCallback + { + private EmsTemplate jmsTemplate; + private Destination destination; + private string destinationName; + + + public ReceiveCallback(EmsTemplate jmsTemplate, string destinationName) + { + this.jmsTemplate = jmsTemplate; + this.destinationName = destinationName; + } + + public ReceiveCallback(EmsTemplate jmsTemplate, Destination destination) + { + this.jmsTemplate = jmsTemplate; + this.destination = destination; + } + + public object DoInEms(Session session) + { + if (destination != null) + { + return jmsTemplate.DoReceive(session, destination, null); + } + else + { + return jmsTemplate.DoReceive(session, + jmsTemplate.ResolveDestinationName(session, destinationName), + null); + } + + } + } + + private class ConvertAndSendMessageCreator : IMessageCreator + { + private EmsTemplate jmsTemplate; + private object objectToConvert; + private IMessagePostProcessor messagePostProcessor; + private MessagePostProcessorDelegate messagePostProcessorDelegate; + + public ConvertAndSendMessageCreator(EmsTemplate jmsTemplate, object message, IMessagePostProcessor messagePostProcessor) + { + this.jmsTemplate = jmsTemplate; + objectToConvert = message; + this.messagePostProcessor = messagePostProcessor; + } + + public ConvertAndSendMessageCreator(EmsTemplate jmsTemplate, object message, MessagePostProcessorDelegate messagePostProcessorDelegate) + { + this.jmsTemplate = jmsTemplate; + objectToConvert = message; + this.messagePostProcessorDelegate = messagePostProcessorDelegate; + } + + public Message CreateMessage(Session session) + { + Message msg = jmsTemplate.MessageConverter.ToMessage(objectToConvert, session); + if (messagePostProcessor != null) + { + return messagePostProcessor.PostProcessMessage(msg); + } + else + { + return messagePostProcessorDelegate(msg); + } + } + } + + private class ReceiveSelectedCallback : ISessionCallback + { + private EmsTemplate jmsTemplate; + private string messageSelector; + private string destinationName; + private Destination destination; + + public ReceiveSelectedCallback(EmsTemplate jmsTemplate, + Destination destination, + string messageSelector) + { + this.jmsTemplate = jmsTemplate; + this.destination = destination; + this.messageSelector = messageSelector; + } + public ReceiveSelectedCallback(EmsTemplate jmsTemplate, + string destinationName, + string messageSelector) + { + this.jmsTemplate = jmsTemplate; + this.destinationName = destinationName; + this.messageSelector = messageSelector; + } + + public object DoInEms(Session session) + { + if (destination != null) + { + return jmsTemplate.DoReceive(session, destination, messageSelector); + } + else + { + return jmsTemplate.DoReceive(session, + jmsTemplate.ResolveDestinationName(session, destinationName), + messageSelector); + } + + } + + } + + private class ExecuteSessionCallbackUsingDelegate : ISessionCallback + { + private SessionDelegate del; + public ExecuteSessionCallbackUsingDelegate(SessionDelegate del) + { + this.del = del; + } + + public object DoInEms(Session session) + { + return del(session); + } + } + + #endregion + } + + /// + /// This is a TIBCO specific class so that we can reuse connections, session, and + /// message producers instead of creating/destroying them on each operation. + /// + internal class EmsResources + { + private Connection connection; + private Session session; + + private IDictionary cachedProducers = new Hashtable(); + private MessageProducer cachedUnspecifiedDestinationMessageProducer; + + public Connection Connection + { + get { return connection; } + set { connection = value; } + } + + public Session Session + { + get { return session; } + set { session = value; } + } + + public MessageProducer UnspecifiedDestinationMessageProducer + { + get { return cachedUnspecifiedDestinationMessageProducer; } + set { cachedUnspecifiedDestinationMessageProducer = value; } + } + + + public IDictionary Producers + { + get { return cachedProducers; } + set { cachedProducers = value; } + } + } + + + internal class SimpleMessageCreator : IMessageCreator + { + private EmsTemplate jmsTemplate; + private object objectToConvert; + + public SimpleMessageCreator(EmsTemplate jmsTemplate, object objectToConvert) + { + this.jmsTemplate = jmsTemplate; + this.objectToConvert = objectToConvert; + } + + public Message CreateMessage(Session session) + { + return jmsTemplate.MessageConverter.ToMessage(objectToConvert, session); + } + + + } + + + + internal class SendDestinationCallback : ISessionCallback + { + private string destinationName; + private Destination destination; + private EmsTemplate jmsTemplate; + private IMessageCreator messageCreator; + private MessageCreatorDelegate messageCreatorDelegate; + + public SendDestinationCallback(EmsTemplate jmsTemplate, string destinationName, IMessageCreator messageCreator) + { + this.jmsTemplate = jmsTemplate; + this.destinationName = destinationName; + this.messageCreator = messageCreator; + } + + public SendDestinationCallback(EmsTemplate jmsTemplate, Destination destination, IMessageCreator messageCreator) + { + this.jmsTemplate = jmsTemplate; + this.destination = destination; + this.messageCreator = messageCreator; + } + + public SendDestinationCallback(EmsTemplate jmsTemplate, string destinationName, MessageCreatorDelegate messageCreatorDelegate) + { + this.jmsTemplate = jmsTemplate; + this.destinationName = destinationName; + this.messageCreatorDelegate = messageCreatorDelegate; + } + + public SendDestinationCallback(EmsTemplate jmsTemplate, Destination destination, MessageCreatorDelegate messageCreatorDelegate) + { + this.jmsTemplate = jmsTemplate; + this.destination = destination; + this.messageCreatorDelegate = messageCreatorDelegate; + } + + + public object DoInEms(Session session) + { + if (destination == null) + { + destination = jmsTemplate.ResolveDestinationName(session, destinationName); + } + if (messageCreator != null) + { + jmsTemplate.DoSend(session, destination, messageCreator); + } + else + { + jmsTemplate.DoSend(session, destination, messageCreatorDelegate); + } + return null; + } + } +} diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IEmsOperations.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IEmsOperations.cs new file mode 100644 index 00000000..18d57c94 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IEmsOperations.cs @@ -0,0 +1,436 @@ +#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 Spring.Messaging.Ems.Core; +using TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// Specifies a basic set of EMS operations. + /// + /// + ///

Implemented by EmsTemplate. Not often used but a useful option + /// to enhance testability, as it can easily be mocked or stubbed.

+ /// + ///

Provides EmsTemplate's send(..) and + /// receive(..) methods that mirror various EMS API methods. + /// See the EMS specification and EMS API docs for details on those methods. + ///

+ ///
+ /// Mark Pollack + /// Juergen Hoeller + /// Mark Pollack (.NET) + public interface IEmsOperations + { + /// Execute the action specified by the given action object within + /// a EMS Session. + /// + /// delegate that exposes the session + /// the result object from working with the session + /// + /// EMSException if there is any problem + object Execute(SessionDelegate del); + + /// Execute the action specified by the given action object within + /// a EMS Session. + /// + /// callback object that exposes the session + /// + /// the result object from working with the session + /// + /// EMSException if there is any problem + object Execute(ISessionCallback action); + + /// Send a message to a EMS destination. The callback gives access to + /// the EMS session and MessageProducer in order to do more complex + /// send operations. + /// + /// callback object that exposes the session/producer pair + /// + /// the result object from working with the session + /// + /// EMSException if there is any problem + object Execute(IProducerCallback action); + + /// Send a message to a EMS destination. The callback gives access to + /// the EMS session and MessageProducer in order to do more complex + /// send operations. + /// + /// delegate that exposes the session/producer pair + /// + /// the result object from working with the session + /// + /// EMSException if there is any problem + object Execute(ProducerDelegate del); + + + //------------------------------------------------------------------------- + // Convenience methods for sending messages + //------------------------------------------------------------------------- + + /// Send a message to the default destination. + ///

This will only work with a default destination specified!

+ ///
+ /// callback to create a message + /// + /// EMSException if there is any problem + void Send(IMessageCreator messageCreator); + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a Session. + /// + /// the destination to send this message to + /// + /// callback to create a message + /// + /// EMSException if there is any problem + void Send(Destination destination, IMessageCreator messageCreator); + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a Session. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// callback to create a message + /// + /// EMSException if there is any problem + void Send(string destinationName, IMessageCreator messageCreator); + + //------------------------------------------------------------------------- + // Convenience methods for sending messages + //------------------------------------------------------------------------- + + /// Send a message to the default destination. + ///

This will only work with a default destination specified!

+ ///
+ /// delegate callback to create a message + /// + /// EMSException if there is any problem + void SendWithDelegate(MessageCreatorDelegate messageCreatorDelegate); + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a Session. + /// + /// the destination to send this message to + /// + /// delegate callback to create a message + /// + /// EMSException if there is any problem + void SendWithDelegate(Destination destination, MessageCreatorDelegate messageCreatorDelegate); + + /// Send a message to the specified destination. + /// The IMessageCreator callback creates the message given a Session. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// delegate callback to create a message + /// + /// EMSException if there is any problem + void SendWithDelegate(string destinationName, MessageCreatorDelegate messageCreatorDelegate); + + //------------------------------------------------------------------------- + // Convenience methods for sending auto-converted messages + //------------------------------------------------------------------------- + + /// Send the given object to the default destination, converting the object + /// to a EMS message with a configured IMessageConverter. + ///

This will only work with a default destination specified!

+ ///
+ /// the object to convert to a message + /// + /// EMSException if there is any problem + void ConvertAndSend(object message); + + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. + /// + /// the destination to send this message to + /// + /// the object to convert to a message + /// + /// EMSException if there is any problem + void ConvertAndSend(Destination destination, object message); + + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the object to convert to a message + /// + /// EMSException if there is any problem + void ConvertAndSend(string destinationName, object message); + + /// Send the given object to the default destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + ///

This will only work with a default destination specified!

+ ///
+ /// the object to convert to a message + /// + /// the callback to modify the message + /// + /// EMSException if there is any problem + void ConvertAndSend(object message, IMessagePostProcessor postProcessor); + + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the destination to send this message to + /// + /// the object to convert to a message + /// + /// the callback to modify the message + /// + /// EMSException if there is any problem + void ConvertAndSend(Destination destination, object message, IMessagePostProcessor postProcessor); + + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the object to convert to a message. + /// + /// the callback to modify the message + /// + /// EMSException if there is any problem + void ConvertAndSend(string destinationName, object message, IMessagePostProcessor postProcessor); + + + /// + /// Send the given object to the default destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + ///

This will only work with a default destination specified!

+ ///
+ /// the object to convert to a message + /// the callback to modify the message + /// EMSException if there is any problem + void ConvertAndSendWithDelegate(object message, MessagePostProcessorDelegate postProcessor); + + /// + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the destination to send this message to + /// the object to convert to a message + /// the callback to modify the message + /// EMSException if there is any problem + void ConvertAndSendWithDelegate(Destination destination, object message, MessagePostProcessorDelegate postProcessor); + + /// + /// Send the given object to the specified destination, converting the object + /// to a EMS message with a configured IMessageConverter. The IMessagePostProcessor + /// callback allows for modification of the message after conversion. + /// + /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// the object to convert to a message. + /// the callback to modify the message + /// EMSException if there is any problem + void ConvertAndSendWithDelegate(string destinationName, object message, MessagePostProcessorDelegate postProcessor); + + + //------------------------------------------------------------------------- + // Convenience methods for receiving messages + //------------------------------------------------------------------------- + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///

This will only work with a default destination specified!

+ ///
+ /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + Message Receive(); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the destination to receive a message from + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + Message Receive(Destination destination); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + Message Receive(string destinationName); + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///

This will only work with a default destination specified!

+ ///
+ /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + Message ReceiveSelected(string messageSelector); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the destination to receive a message from + /// + /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + Message ReceiveSelected(Destination destination, string messageSelector); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message received by the consumer, or null if the timeout expires + /// + /// EMSException if there is any problem + Message ReceiveSelected(string destinationName, string messageSelector); + + + //------------------------------------------------------------------------- + // Convenience methods for receiving auto-converted messages + //------------------------------------------------------------------------- + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///

This will only work with a default destination specified!

+ ///
+ /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + object ReceiveAndConvert(); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the destination to receive a message from + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + object ReceiveAndConvert(Destination destination); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + object ReceiveAndConvert(string destinationName); + + /// Receive a message synchronously from the default destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///

This will only work with a default destination specified!

+ ///
+ /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + object ReceiveSelectedAndConvert(string messageSelector); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the destination to receive a message from + /// + /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + object ReceiveSelectedAndConvert(Destination destination, string messageSelector); + + /// Receive a message synchronously from the specified destination, but only + /// wait up to a specified time for delivery. Convert the message into an + /// object with a configured IMessageConverter. + ///

This method should be used carefully, since it will block the thread + /// until the message becomes available or until the timeout value is exceeded.

+ ///
+ /// the name of the destination to send this message to + /// (to be resolved to an actual destination by a DestinationResolver) + /// + /// the EMS message selector expression (or null if none). + /// See the EMS specification for a detailed definition of selector expressions. + /// + /// the message produced for the consumer or null if the timeout expires. + /// + /// EMSException if there is any problem + object ReceiveSelectedAndConvert(string destinationName, string messageSelector); + } +} diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IMessageCreator.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IMessageCreator.cs new file mode 100644 index 00000000..620fc10b --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IMessageCreator.cs @@ -0,0 +1,42 @@ +#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 TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// Creates a EMS message given a Session + /// + ///

The Session typically is provided by an instance + /// of the EmsTemplate class.

+ ///
+ /// Mark Pollack + public interface IMessageCreator + { + /// Create a Message to be sent. + /// the EMS Session to be used to create the + /// Message (never null) + /// + /// the Message to be sent + /// + /// EMSException if thrown by EMS API methods + Message CreateMessage(Session session); + } +} diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IMessagePostProcessor.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IMessagePostProcessor.cs new file mode 100644 index 00000000..7b0008b9 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IMessagePostProcessor.cs @@ -0,0 +1,46 @@ +#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 TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// To be used with EmsTemplate's send method that + /// convert an object to a message. + /// + /// + /// It allows for further modification of the message after it has been processed + /// by the converter. This is useful for setting of EMS Header and Properties. + /// + /// Mark Pollack + public interface IMessagePostProcessor + { + /// Apply a IMessagePostProcessor to the message. The returned message is + /// typically a modified version of the original. + /// + /// the EMS message from the IMessageConverter + /// + /// the modified version of the Message + /// + /// EMSException if thrown by EMS API methods + Message PostProcessMessage(Message message); + + } +} diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IProducerCallback.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IProducerCallback.cs new file mode 100644 index 00000000..4bb97203 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/IProducerCallback.cs @@ -0,0 +1,48 @@ +#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 TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// Callback for sending a message to a EMS destination. + /// + ///

To be used with the EmsTemplate.Execute(IProducerCallback) + /// method, often implemented as an anonymous inner class.

+ /// + ///

The typical implementation will perform multiple operations on the + /// supplied EMS Session and MessageProducer.

+ ///
+ /// Mark Pollack + public interface IProducerCallback + { + /// Perform operations on the given Session and MessageProducer. + /// The message producer is not associated with any destination. + /// + /// the EMS Session object to use + /// + /// the EMS MessageProducer object to use + /// + /// a result object from working with the Session, if any (can be null) + /// + object DoInEms(Session session, MessageProducer producer); + + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/ISessionCallback.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/ISessionCallback.cs new file mode 100644 index 00000000..53ff5678 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/ISessionCallback.cs @@ -0,0 +1,47 @@ +#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 TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// Callback for executing any number of operations on a provided + /// Session + /// + /// + ///

To be used with the EmsTemplate.Execute(ISessionCallback)} + /// method, often implemented as an anonymous inner class.

+ ///
+ /// Mark Pollack + /// + /// + public interface ISessionCallback + { + /// Execute any number of operations against the supplied EMS + /// Session, possibly returning a result. + /// + /// the EMS Session + /// + /// a result object from working with the Session, if any (so can be null) + /// + /// EMSException if there is any problem + object DoInEms(Session session); + } +} diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/MessageCreatorDelegate.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/MessageCreatorDelegate.cs new file mode 100644 index 00000000..fd12d227 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/MessageCreatorDelegate.cs @@ -0,0 +1,35 @@ +#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 TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// + /// Delegate that creates a EMS message given a Session + /// + /// the EMS Session to be used to create the + /// Message (never null) + /// + /// the Message to be sent + /// + /// EMSException if thrown by EMS API methods + public delegate Message MessageCreatorDelegate(Session session); +} diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/MessagePostProcessorDelegate.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/MessagePostProcessorDelegate.cs new file mode 100644 index 00000000..ba8a4f26 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/MessagePostProcessorDelegate.cs @@ -0,0 +1,36 @@ +#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 TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + + /// + /// Delegate that is used with EmsTemplate's ConvertAndSend method that converts + /// an object. + /// + /// + /// It allows for further modification of the message after it has been processed + /// by the converter. This is useful for setting of EMS Header and Properties. + /// + /// Mark Pollack + public delegate Message MessagePostProcessorDelegate(Message message); +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/ProducerDelegate.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/ProducerDelegate.cs new file mode 100644 index 00000000..d7bf6cd7 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/ProducerDelegate.cs @@ -0,0 +1,38 @@ + + +#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 TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// Perform operations on the given Session and MessageProducer. + /// The message producer is not associated with any destination. + /// + /// the EMS Session object to use + /// + /// the EMS MessageProducer object to use + /// + /// a result object from working with the Session, if any (can be null) + /// + public delegate object ProducerDelegate(Session session, MessageProducer producer); + +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/SessionDelegate.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/SessionDelegate.cs new file mode 100644 index 00000000..c79cc6fd --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Core/SessionDelegate.cs @@ -0,0 +1,39 @@ +#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 TIBCO.EMS; + +namespace Spring.Messaging.Ems.Core +{ + /// + /// Callback delegate for code that operates on a Session. + /// + /// The EMS Session object. + /// + /// Allows you to execute any number of operations + /// on a single ISession, possibly returning a result a result. + /// + /// + /// A result object from working with the Session, if any (so can be null) + /// + /// EMSException if there is any problem + /// Mark Pollack + public delegate object SessionDelegate(Session session); +} diff --git a/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/RecoveryTimeExceededException.cs b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/RecoveryTimeExceededException.cs new file mode 100644 index 00000000..d6ba4018 --- /dev/null +++ b/src/Spring/Spring.Messaging.Ems/Messaging/Ems/Listener/RecoveryTimeExceededException.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; +using Spring.Messaging.Ems.Listener.Adapter; +using TIBCO.EMS; + +namespace Spring.Messaging.Ems.Listener +{ + /// + /// Exception thrown when the maximum connection recovery time has been exceeded. + /// + /// Mark Pollack + public class RecoveryTimeExceededException : EMSException + { + + /// + /// Initializes a new instance of the class, with the specified message + /// + /// The message. + public RecoveryTimeExceededException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class, with the specified message + /// and root cause exception + /// + /// The message. + /// The inner exception. + public RecoveryTimeExceededException(string message, Exception innerException) + : base(message) + { + LinkedException = innerException; + } + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Config/EmsNamespaceHandlerTests.cs b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Config/EmsNamespaceHandlerTests.cs new file mode 100644 index 00000000..6d289229 --- /dev/null +++ b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Config/EmsNamespaceHandlerTests.cs @@ -0,0 +1,106 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System.Collections; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Context; +using Spring.Context.Support; +using Spring.Messaging.Ems.Listener; +using Spring.Objects.Factory.Xml; +using TIBCO.EMS; + +#endregion + +namespace Spring.Messaging.Ems.Config +{ + /// + /// This class contains tests for + /// + /// Mark Pollack + /// $Id:$ + [TestFixture] + public class EmsNamespaceHandlerTests + { + + private static string DEFAULT_CONNECTION_FACTORY = "ConnectionFactory"; + + private static string EXPLICIT_CONNECTION_FACTORY = "testConnectionFactory"; + + + private IApplicationContext ctx; + + private MockRepository mocks; + + [SetUp] + public void Setup() + { + NamespaceParserRegistry.RegisterParser(typeof(EmsNamespaceParser)); + ctx = new XmlApplicationContext(ReadOnlyXmlTestResource.GetFilePath("EmsNamespaceHandlerTests.xml", GetType())); + mocks = new MockRepository(); + } + + [Test] + public void Registered() + { + Assert.IsNotNull(NamespaceParserRegistry.GetParser("http://www.springframework.net/ems")); + } + + [Test] + public void ObjectsCreated() + { + IDictionary containers = ctx.GetObjectsOfType(typeof(SimpleMessageListenerContainer)); + Assert.AreEqual(3, containers.Count); + } + + [Test] + public void ContainerConfiguration() + { + IDictionary containers = ctx.GetObjectsOfType(typeof (SimpleMessageListenerContainer)); + ConnectionFactory defaultConnectionFactory = (ConnectionFactory) ctx.GetObject(DEFAULT_CONNECTION_FACTORY); + ConnectionFactory explicitConnectionFactory = (ConnectionFactory) ctx.GetObject(EXPLICIT_CONNECTION_FACTORY); + + + int defaultConnectionFactoryCount = 0; + int explicitConnectionFactoryCount = 0; + foreach (DictionaryEntry dictionaryEntry in containers) + { + SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) dictionaryEntry.Value; + if (container.ConnectionFactory.Equals(defaultConnectionFactory)) + { + defaultConnectionFactoryCount++; + } + else if (container.ConnectionFactory.Equals(explicitConnectionFactory)) + { + explicitConnectionFactoryCount++; + } + } + + Assert.AreEqual(1, defaultConnectionFactoryCount, "1 container should have the default connectionFactory"); + Assert.AreEqual(2, explicitConnectionFactoryCount, "2 containers should have the explicit connectionFactory"); + + } + + + + } +} \ 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 new file mode 100644 index 00000000..1fdf1da6 --- /dev/null +++ b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Config/EmsNamespaceHandlerTests.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Connections/TestMessageListener.cs b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Connections/TestMessageListener.cs new file mode 100644 index 00000000..52e2c2dc --- /dev/null +++ b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Connections/TestMessageListener.cs @@ -0,0 +1,52 @@ +#region License + +/* + * Copyright 2002-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +using TIBCO.EMS; + +namespace Spring.Messaging.Ems.Connections +{ + /// + /// + /// + /// + /// + /// + /// Mark Pollack + public class TestMessageListener : IMessageListener + { + private string message; + + + public string Message + { + get { return message; } + set { message = value; } + } + + #region IMessageListener Members + + public void OnMessage(Message message) + { + this.message = "Test1"; + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/LoggingExceptionHandler.cs b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/LoggingExceptionHandler.cs new file mode 100644 index 00000000..2f1853d1 --- /dev/null +++ b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/LoggingExceptionHandler.cs @@ -0,0 +1,28 @@ +using Common.Logging; +using TIBCO.EMS; + +namespace Spring.Messaging.Ems.Integration +{ + /// + /// + /// + public class LoggingExceptionHandler : IExceptionListener + { + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof (LoggingExceptionHandler)); + + #endregion + + + #region IExceptionListener Members + + public void OnException(EMSException e) + { + LOG.Error("Exception processing message", e); + } + + #endregion + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/SimpleMessageListener.cs b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/SimpleMessageListener.cs new file mode 100644 index 00000000..909d78b7 --- /dev/null +++ b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/SimpleMessageListener.cs @@ -0,0 +1,38 @@ +using Common.Logging; +using TIBCO.EMS; + +namespace Spring.Messaging.Ems.Integration +{ + public class SimpleMessageListener : IMessageListener + { + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof(SimpleMessageListener)); + #endregion + + private Message lastReceivedMessage; + private int messageCount; + + public Message LastReceivedMessage + { + get { return lastReceivedMessage; } + } + + + public int MessageCount + { + get { return messageCount; } + } + + #region IMessageListener Members + + public void OnMessage(Message message) + { + lastReceivedMessage = message; + messageCount++; + LOG.Debug("Message listener count = " + messageCount); + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/SimpleMessageListenerContainerTests.cs b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/SimpleMessageListenerContainerTests.cs new file mode 100644 index 00000000..cd58cb97 --- /dev/null +++ b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/SimpleMessageListenerContainerTests.cs @@ -0,0 +1,90 @@ +#region License + +/* + * Copyright © 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#endregion + +#region Imports + +using System; +using System.Threading; +using NUnit.Framework; +using Spring.Messaging.Ems.Core; +using Spring.Messaging.Ems.Listener; +using Spring.Testing.NUnit; + +#endregion + +namespace Spring.Messaging.Ems.Integration +{ + /// + /// This class contains integration tests for the SimpleMessageListenerContainer + /// + /// Mark Pollack + /// $Id:$ + [TestFixture] + public class SimpleMessageListenerContainerTests : AbstractDependencyInjectionSpringContextTests + { + + [Test] + [Explicit] + public void SendAndAsyncReceive() + { + SimpleMessageListenerContainer container = + (SimpleMessageListenerContainer) applicationContext["SimpleMessageListenerContainer"]; + SimpleMessageListener listener = applicationContext["SimpleMessageListener"] as SimpleMessageListener; + Assert.IsNotNull(container); + Assert.IsNotNull(listener); + + + EmsTemplate emsTemplate = (EmsTemplate) applicationContext["MessageTemplate"] as EmsTemplate; + Assert.IsNotNull(emsTemplate); + + Assert.AreEqual(0, listener.MessageCount); + emsTemplate.ConvertAndSend("Hello World 1"); + + int waitInMillis = 2000; + Thread.Sleep(waitInMillis); + Assert.AreEqual(1,listener.MessageCount); + + container.Stop(); + Console.WriteLine("container stopped."); + emsTemplate.ConvertAndSend("Hello World 2"); + Thread.Sleep(waitInMillis); + Assert.AreEqual(1, listener.MessageCount); + + container.Start(); + Console.WriteLine("container started."); + Thread.Sleep(waitInMillis); + Assert.AreEqual(2, listener.MessageCount); + + container.Shutdown(); + + Thread.Sleep(waitInMillis); + + + } + + + protected override string[] ConfigLocations + { + get { return new string[] { "assembly://Spring.Messaging.Ems.Tests/Spring.Messaging.Ems.Integration/SimpleMessageListenerContainerTests.xml" }; } + } + + + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/SimpleMessageListenerContainerTests.xml b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/SimpleMessageListenerContainerTests.xml new file mode 100644 index 00000000..cfd1033c --- /dev/null +++ b/test/Spring/Spring.Messaging.Ems.Tests/Messaging/Ems/Integration/SimpleMessageListenerContainerTests.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file