diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs index d068c7d1..c231b730 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ConnectionFactoryUtils.cs @@ -19,9 +19,10 @@ #endregion using System; +using Apache.NMS; +using Common.Logging; using Spring.Transaction.Support; using Spring.Util; -using Apache.NMS; namespace Spring.Messaging.Nms.IConnections { @@ -33,7 +34,11 @@ namespace Spring.Messaging.Nms.IConnections /// Mark Pollack (.NET) public abstract class ConnectionFactoryUtils { - + #region Logging + + private static readonly ILog LOG = LogManager.GetLogger(typeof(ConnectionFactoryUtils)); + + #endregion /// Obtain a NMS ISession that is synchronized with the current transaction, if any. /// the IConnectionFactory to obtain a ISession for @@ -50,10 +55,13 @@ namespace Spring.Messaging.Nms.IConnections /// the transactional ISession, or null if none found /// /// NMSException in case of NMS failure - public static ISession GetTransactionalSession(IConnectionFactory cf, IConnection existingCon, bool synchedLocalTransactionAllowed) + public static ISession GetTransactionalSession(IConnectionFactory cf, IConnection existingCon, + bool synchedLocalTransactionAllowed) { - - return DoGetTransactionalSession(cf, new AnonymousClassResourceFactory(existingCon, cf, synchedLocalTransactionAllowed)); + return + DoGetTransactionalSession(cf, + new AnonymousClassResourceFactory(existingCon, cf, + synchedLocalTransactionAllowed)); } /// Obtain a NMS ISession that is synchronized with the current transaction, if any. @@ -66,13 +74,13 @@ namespace Spring.Messaging.Nms.IConnections /// the transactional ISession, or null if none found /// /// NMSException in case of NMS failure - public static ISession DoGetTransactionalSession(System.Object resourceKey, ConnectionFactoryUtils.ResourceFactory resourceFactory) + public static ISession DoGetTransactionalSession(Object resourceKey, ResourceFactory resourceFactory) { - AssertUtils.ArgumentNotNull(resourceKey, "Resource key must not be null"); AssertUtils.ArgumentNotNull(resourceKey, "ResourceFactory must not be null"); - NmsResourceHolder resourceHolder = (NmsResourceHolder)TransactionSynchronizationManager.GetResource(resourceKey); + NmsResourceHolder resourceHolder = + (NmsResourceHolder)TransactionSynchronizationManager.GetResource(resourceKey); if (resourceHolder != null) { ISession rssession = resourceFactory.GetSession(resourceHolder); @@ -90,7 +98,7 @@ namespace Spring.Messaging.Nms.IConnections { conHolderToUse = new NmsResourceHolder(); } - Apache.NMS.IConnection con = resourceFactory.GetConnection(conHolderToUse); + IConnection con = resourceFactory.GetConnection(conHolderToUse); ISession session = null; try { @@ -115,7 +123,7 @@ namespace Spring.Messaging.Nms.IConnections { session.Close(); } - catch (System.Exception) + catch (Exception) { // ignore } @@ -126,7 +134,7 @@ namespace Spring.Messaging.Nms.IConnections { con.Close(); } - catch (System.Exception) + catch (Exception) { // ignore } @@ -135,52 +143,90 @@ namespace Spring.Messaging.Nms.IConnections } if (conHolderToUse != resourceHolder) { - TransactionSynchronizationManager.RegisterSynchronization(new NmsResourceSynchronization(resourceKey, conHolderToUse, resourceFactory.SynchedLocalTransactionAllowed)); + TransactionSynchronizationManager.RegisterSynchronization( + new NmsResourceSynchronization(resourceKey, conHolderToUse, + resourceFactory.SynchedLocalTransactionAllowed)); conHolderToUse.SynchronizedWithTransaction = true; TransactionSynchronizationManager.BindResource(resourceKey, conHolderToUse); } return session; } - - + public static void ReleaseConnection(IConnection connection, IConnectionFactory cf, bool started) + { + if (connection == null) + { + return; + } + + if (started && cf is ISmartConnectionFactory && ((ISmartConnectionFactory)cf).ShouldStop(connection)) + { + try + { + connection.Stop(); + } + catch (Exception ex) + { + LOG.Debug("Could not stop NMS IConnection before closing it", ex); + + } + } + try + { + connection.Close(); + } catch (Exception ex) + { + LOG.Debug("Could not close NMS Connection", ex); + } + } + + public static bool IsSessionTransactional(ISession session, IConnectionFactory cf) + { + if (session == null || cf == null) + { + return false; + } + NmsResourceHolder resourceHolder = (NmsResourceHolder) TransactionSynchronizationManager.GetResource(cf); + return (resourceHolder != null && resourceHolder.ContainsSession(session)); + } #region ResourceFactory helper classes - private class AnonymousClassResourceFactory : ConnectionFactoryUtils.ResourceFactory + private class AnonymousClassResourceFactory : ResourceFactory { private IConnection existingCon; private IConnectionFactory cf; private bool synchedLocalTransactionAllowed; - public AnonymousClassResourceFactory(Apache.NMS.IConnection existingCon, IConnectionFactory cf, bool synchedLocalTransactionAllowed) + public AnonymousClassResourceFactory(IConnection existingCon, IConnectionFactory cf, + bool synchedLocalTransactionAllowed) { InitBlock(existingCon, cf, synchedLocalTransactionAllowed); } - private void InitBlock(Apache.NMS.IConnection existingCon, IConnectionFactory cf, bool synchedLocalTransactionAllowed) + private void InitBlock(IConnection existingCon, IConnectionFactory cf, bool synchedLocalTransactionAllowed) { this.existingCon = existingCon; this.cf = cf; this.synchedLocalTransactionAllowed = synchedLocalTransactionAllowed; } - + public virtual ISession GetSession(NmsResourceHolder holder) { - return holder.GetSession(typeof(ISession), existingCon); + return holder.GetSession(typeof(ISession), existingCon); } - public virtual Apache.NMS.IConnection GetConnection(NmsResourceHolder holder) + public virtual IConnection GetConnection(NmsResourceHolder holder) { - return (existingCon != null ? existingCon : holder.GetConnection()); + return (existingCon != null ? existingCon : holder.GetConnection()); } - public virtual Apache.NMS.IConnection CreateConnection() + public virtual IConnection CreateConnection() { return cf.CreateConnection(); } - public virtual ISession CreateSession(Apache.NMS.IConnection con) + public virtual ISession CreateSession(IConnection con) { if (synchedLocalTransactionAllowed) { @@ -190,7 +236,6 @@ namespace Spring.Messaging.Nms.IConnections { return con.CreateSession(AcknowledgementMode.AutoAcknowledge); } - } public bool SynchedLocalTransactionAllowed @@ -198,6 +243,7 @@ namespace Spring.Messaging.Nms.IConnections get { return synchedLocalTransactionAllowed; } } } + #endregion #region Helper classes/interfaces @@ -207,7 +253,6 @@ namespace Spring.Messaging.Nms.IConnections /// public interface ResourceFactory { - /// Fetch an appropriate ISession from the given NmsResourceHolder. /// the NmsResourceHolder /// @@ -236,7 +281,7 @@ namespace Spring.Messaging.Nms.IConnections /// the new NMS ISession /// /// NMSException if thrown by NMS API methods - ISession CreateSession(Apache.NMS.IConnection con); + ISession CreateSession(IConnection con); /// @@ -247,17 +292,13 @@ namespace Spring.Messaging.Nms.IConnections /// Returns whether to allow for synchronizing a local NMS transaction /// /// - bool SynchedLocalTransactionAllowed - { - get; - } + bool SynchedLocalTransactionAllowed { get; } } /// Callback for resource cleanup at the end of a non-native NMS transaction /// private class NmsResourceSynchronization : TransactionSynchronizationAdapter { - private object resourceKey; private NmsResourceHolder resourceHolder; @@ -275,7 +316,7 @@ namespace Spring.Messaging.Nms.IConnections public override void Suspend() { - if (this.holderActive) + if (holderActive) { TransactionSynchronizationManager.UnbindResource(resourceKey); } @@ -283,7 +324,7 @@ namespace Spring.Messaging.Nms.IConnections public override void Resume() { - if (this.holderActive) + if (holderActive) { TransactionSynchronizationManager.BindResource(resourceKey, resourceHolder); } @@ -291,22 +332,22 @@ namespace Spring.Messaging.Nms.IConnections public override void BeforeCompletion() { - TransactionSynchronizationManager.UnbindResource(this.resourceKey); - this.holderActive = false; + TransactionSynchronizationManager.UnbindResource(resourceKey); + holderActive = false; if (!transacted) { - this.resourceHolder.CloseAll(); + resourceHolder.CloseAll(); } } //TODO bring in new Spring.Data library to Integration project which has this method in interface. public override void AfterCommit() { - if (this.transacted) + if (transacted) { try { - this.resourceHolder.CommitAll(); + resourceHolder.CommitAll(); } catch (NMSException ex) { @@ -317,12 +358,13 @@ namespace Spring.Messaging.Nms.IConnections public override void AfterCompletion(TransactionSynchronizationStatus status) { - if (this.transacted) + if (transacted) { - this.resourceHolder.CloseAll(); + resourceHolder.CloseAll(); } } } + #endregion } -} +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ISmartConnectionFactory.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ISmartConnectionFactory.cs new file mode 100644 index 00000000..67427edd --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/ISmartConnectionFactory.cs @@ -0,0 +1,42 @@ + + +#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 Apache.NMS; + +namespace Spring.Messaging.Nms.IConnections +{ + /// + /// Extension of the IConnectionFactory interface, + /// indicating how to release Connections obtained from it. + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public interface ISmartConnectionFactory + { + /// + /// Shoulds we stop the connection, obtained from this ConnectionFactory? + /// + /// The connection to check. + /// wheter a stop call is necessary + bool ShouldStop(IConnection con); + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs index adb83d62..fcf816af 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsResourceHolder.cs @@ -54,6 +54,7 @@ namespace Spring.Messaging.Nms.IConnections private IList sessions = new LinkedList(); private IDictionary sessionsPerIConnection = new Hashtable(); + private IConnectionFactory connectionFactory; #endregion @@ -71,12 +72,20 @@ namespace Spring.Messaging.Nms.IConnections /// /// the NMS ISession /// - public NmsResourceHolder(Apache.NMS.IConnection connection, ISession session) + public NmsResourceHolder(Apache.NMS.IConnection connection, ISession session) { AddConnection(connection); AddSession(session, connection); this.frozen = true; } + + public NmsResourceHolder(IConnectionFactory connectionFactory, IConnection connection, ISession session) + { + this.connectionFactory = connectionFactory; + AddConnection(connection); + AddSession(session, connection); + this.frozen = true; + } #endregion #region Properties @@ -191,25 +200,17 @@ namespace Spring.Messaging.Nms.IConnections logger.Debug("Could not close NMS ISession after transaction", ex); } } - foreach (Apache.NMS.IConnection connection in connections) + foreach (IConnection connection in connections) { - try - { - try - { - connection.Stop(); - } - finally - { - connection.Close(); - } - } - catch (Exception ex) - { - logger.Debug("Could not close NMS IConnection after transaction", ex); - } + ConnectionFactoryUtils.ReleaseConnection(connection, connectionFactory, true); } } + + public bool ContainsSession(ISession session) + { + return this.sessions.Contains(session); + } + #endregion } } diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsTransactionManager.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsTransactionManager.cs new file mode 100644 index 00000000..c1fcb32c --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/NmsTransactionManager.cs @@ -0,0 +1,315 @@ + + +using System; +using System.Data; +using Apache.NMS; +using Common.Logging; +using Spring.Objects.Factory; +using Spring.Transaction; +using Spring.Transaction.Support; + +namespace Spring.Messaging.Nms.IConnections +{ + /// + /// A implementation + /// for a single NMS Apache.NMS.IConnectionFactory. Binds a + /// Connection/Session pair from the specified ConnecctionFactory to the thread, + /// potentially allowing for one thread-bound Session per ConnectionFactory. + /// + /// + /// + /// Application code is required to retrieve the transactional Session via + /// . Spring's + /// will autodetect a thread-bound Session and + /// automatically participate in it. + /// + /// + /// This transaction strategy will typically be used in combination with + /// , which uses a single NMS Connection + /// for all NMS access in order to avoid the overhead of repeated Connection + /// creation. Each transaction will then share the same NMS Connection, while still using + /// its own individual Session. + /// + /// + /// Transaction synchronization is turned off by default, as this manager might be used + /// alongside an IDbProvider based Spring transaction manager such as the + /// AdoPlatformTransactionManager, which has stronger needs for synchronization. + /// + /// + /// Juergen Hoeller + /// Mark Pollack (.NET) + public class NmsTransactionManager : AbstractPlatformTransactionManager, + IResourceTransactionManager, IInitializingObject + { + + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof(NmsTransactionManager)); + + #endregion + + private IConnectionFactory connectionFactory; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The ConnectionFactory has to be set before using the instance. + /// This constructor can be used to prepare a NmsTemplate via a ApplicationContext, + /// typically setting the ConnectionFactory via ConnectionFactory property. + /// + /// Turns off transaction synchronization by default, as this manager might + /// be used alongside a dbprovider-based Spring transaction manager like + /// AdoPlatformTransactionManager, which has stronger needs for synchronization. + /// Only one manager is allowed to drive synchronization at any point of time. + /// + /// + public NmsTransactionManager() + { + TransactionSynchronization = TransactionSynchronizationState.Never; + } + + /// + /// Initializes a new instance of the class + /// given a ConnectionFactory. + /// + /// The connection factory to obtain connections from. + public NmsTransactionManager(IConnectionFactory connectionFactory) : this() + { + ConnectionFactory = connectionFactory; + AfterPropertiesSet(); + } + + + /// + /// Gets or sets the connection factory that this instance should manage transaction. + /// for. + /// + /// The connection factory. + public IConnectionFactory ConnectionFactory + { + get { return connectionFactory; } + set + { + //TODO if create TransactionAwareConnectionFactoryProxy need to check for it here. + connectionFactory = value; + } + } + + #region IInitializingObject Members + + /// + /// Make sure the ConnectionFactory has been set. + /// + public void AfterPropertiesSet() + { + if (ConnectionFactory == null) + { + throw new ArgumentException("Property 'ConnectionFactory' is required."); + } + } + + #endregion + + #region IResourceTransactionManager Members + + /// + /// Gets the resource factory that this transaction manager operates on, + /// In tihs case the ConnectionFactory + /// + /// The ConnectionFactory. + public object ResourceFactory + { + get { return ConnectionFactory; } + } + + #endregion + + + protected override object DoGetTransaction() + { + NmsTransactionObject txObject = new NmsTransactionObject(); + + txObject.ResourceHolder = + (NmsResourceHolder) TransactionSynchronizationManager.GetResource(ConnectionFactory); + return txObject; + } + + protected override void DoBegin(object transaction, ITransactionDefinition definition) + { + //This is the default value defined in DefaultTransactionDefinition + if (definition.TransactionIsolationLevel != IsolationLevel.ReadCommitted) + { + throw new InvalidIsolationLevelException("NMS does not support an isoliation level concept"); + } + NmsTransactionObject txObject = (NmsTransactionObject) transaction; + IConnection con = null; + ISession session = null; + try + { + con = CreateConnection(); + session = CreateSession(con); + if (LOG.IsDebugEnabled) + { + log.Debug("Created NMS transaction on Session [" + session + "] from Connection [" + con + "]"); + } + txObject.ResourceHolder = new NmsResourceHolder(ConnectionFactory, con, session); + txObject.ResourceHolder.SynchronizedWithTransaction = true; + int timeout = DetermineTimeout(definition); + if (timeout != DefaultTransactionDefinition.TIMEOUT_DEFAULT) + { + txObject.ResourceHolder.TimeoutInSeconds = timeout; + } + TransactionSynchronizationManager.BindResource(ConnectionFactory, txObject.ResourceHolder); + + + + } catch (NMSException ex) + { + if (session != null) + { + try + { + session.Close(); + } catch (Exception) + {} + } + if (con != null) + { + try + { + con.Close(); + } catch (Exception){} + } + throw new CannotCreateTransactionException("Could not create NMS Transaction", ex); + } + + } + + protected override object DoSuspend(object transaction) + { + NmsTransactionObject txObject = (NmsTransactionObject) transaction; + txObject.ResourceHolder = null; + return TransactionSynchronizationManager.UnbindResource(ConnectionFactory); + } + + protected override void DoResume(object transaction, object suspendedResources) + { + NmsResourceHolder conHolder = (NmsResourceHolder) suspendedResources; + TransactionSynchronizationManager.BindResource(ConnectionFactory, conHolder); + } + + protected override void DoCommit(DefaultTransactionStatus status) + { + NmsTransactionObject txObject = (NmsTransactionObject)status.Transaction; + ISession session = txObject.ResourceHolder.GetSession(); + try + { + if (status.Debug) + { + LOG.Debug("Committing NMS transaction on Session [" + session + "]"); + } + session.Commit(); + ///https://issues.apache.org/activemq/browse/AMQNET-93 + ///TODO - need to mirror JMS exception classes in NMS API. + //} catch (TransactionRolledBackException ex) + //{ + + } + catch (NMSException ex) + { + throw new TransactionSystemException("Could not commit NMS transaction.", ex); + } + } + + protected override void DoRollback(DefaultTransactionStatus status) + { + NmsTransactionObject txObject = (NmsTransactionObject)status.Transaction; + ISession session = txObject.ResourceHolder.GetSession(); + try + { + if (status.Debug) + { + LOG.Debug("Rolling back NMS transaction on Session [" + session + "]"); + } + session.Rollback(); + } + catch (NMSException ex) + { + throw new TransactionSystemException("Could not roll back NMS transaction.", ex); + } + } + + + protected override void DoSetRollbackOnly(DefaultTransactionStatus status) + { + NmsTransactionObject txObject = (NmsTransactionObject)status.Transaction; + txObject.ResourceHolder.RollbackOnly = true; + } + + protected override void DoCleanupAfterCompletion(object transaction) + { + NmsTransactionObject txObject = (NmsTransactionObject)transaction; + TransactionSynchronizationManager.UnbindResource(ConnectionFactory); + txObject.ResourceHolder.CloseAll(); + txObject.ResourceHolder.Clear(); + } + + protected override bool IsExistingTransaction(object transaction) + { + NmsTransactionObject txObject = transaction as NmsTransactionObject; + if (txObject != null) + { + return txObject.ResourceHolder != null; + } + return false; + } + + /// + /// Creates the connection via thie manager's ConnectionFactory. + /// + /// The new Connection + /// If thrown by underlying messaging APIs + protected virtual IConnection CreateConnection() + { + return ConnectionFactory.CreateConnection(); + } + + /// + /// Creates the session for the given Connection + /// + /// The connection to create a Session for. + /// the new Session + /// If thrown by underlying messaging APIs + protected virtual ISession CreateSession(IConnection connection) + { + return connection.CreateSession(AcknowledgementMode.Transactional); + } + + + /// + /// NMS Transaction object, representing a NmsResourceHolder. + /// Used as transaction object by NmsTransactionManager + /// + internal class NmsTransactionObject : ISmartTransactionObject + { + private NmsResourceHolder resourceHolder; + + + public NmsResourceHolder ResourceHolder + { + get { return resourceHolder; } + set { resourceHolder = value; } + } + + #region ISmartTransactionObject Members + + public bool RollbackOnly + { + get { return resourceHolder.RollbackOnly; } + } + + #endregion + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SingleConnectionFactory.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SingleConnectionFactory.cs new file mode 100644 index 00000000..0cbe336b --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Connections/SingleConnectionFactory.cs @@ -0,0 +1,363 @@ +#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 Apache.NMS; +using Common.Logging; +using Spring.Objects.Factory; +using Spring.Util; + +namespace Spring.Messaging.Nms.IConnections +{ + public class SingleConnectionFactory : IConnectionFactory, IInitializingObject, IDisposable + { + #region Logging Definition + + private static readonly ILog LOG = LogManager.GetLogger(typeof (SingleConnectionFactory)); + + #endregion + + private IConnectionFactory targetConnectionFactory; + + private string clientId; + + private ExceptionListener exceptionListenerDelegate; + + private bool reconnectOnException = false; + + /// + /// Wrapped Connection + /// + private IConnection target; + + /// + /// Proxy Connection + /// + private IConnection connection; + + /// + /// Synchronization monitor for the shared Connection + /// + private object connectionMonitor = new object(); + + + /// + /// Initializes a new instance of the class. + /// + public SingleConnectionFactory() + { + } + + + /// + /// Initializes a new instance of the class + /// that alwasy returns the given Connection. + /// + /// The single Connection. + public SingleConnectionFactory(IConnection target) + { + AssertUtils.ArgumentNotNull(target, "connection", "Target Connection must not be null"); + this.target = target; + connection = GetSharedConnection(target); + } + + + /// + /// Initializes a new instance of the class + /// that alwasy returns a single Connection. + /// + /// The target connection factory. + public SingleConnectionFactory(IConnectionFactory targetConnectionFactory) + { + AssertUtils.ArgumentNotNull(targetConnectionFactory, "targetConnectionFactory", + "Target ConnectionFactory must not be null"); + this.targetConnectionFactory = targetConnectionFactory; + } + + /// + /// Gets or sets the target connection factory which will be used to create a single + /// connection. + /// + /// The target connection factory. + public IConnectionFactory TargetConnectionFactory + { + get { return targetConnectionFactory; } + set { targetConnectionFactory = value; } + } + + + /// + /// Gets or sets the client id for the single Connection created and exposed by + /// this ConnectionFactory. + /// + /// Note that the client IDs need to be unique among all active + /// Connections of teh underlying provider. Furthermore, a client ID can only + /// be assigned if the original ConnectionFactory hasn't already assigned one. + /// The client id. + public string ClientId + { + get { return clientId; } + set { clientId = value; } + } + + + /// + /// Gets or sets the exception listener delegate that should be registered with + /// the single connection created by this factory. + /// + /// The exception listener delegate. + public ExceptionListener ExceptionListenerDelegate + { + get { return exceptionListenerDelegate; } + set { exceptionListenerDelegate = value; } + } + + + /// + /// Gets or sets a value indicating whether the single Connection + /// should be reset (to be subsequently renewed) when a NMSException + /// is reported by the underlying Connection. + /// + /// + /// Default is false. Switch this to true + /// to automatically trigger recover based on your messaging provider's + /// exception notifications. + /// + /// Internally, this will lead to a special ExceptionListener (this + /// SingleConnectionFactory itself) being registered with the underlying + /// Connection. This can also be combined with a user-specified + /// ExceptionListener, if desired. + /// + /// + /// + /// true if [reconnect on exception]; otherwise, false. + /// + public bool ReconnectOnException + { + get { return reconnectOnException; } + set { reconnectOnException = value; } + } + + #region IConnectionFactory Members + + public IConnection CreateConnection() + { + lock (connectionMonitor) + { + if (connection == null) + { + InitConnection(); + } + return connection; + } + } + + public void InitConnection() + { + if (TargetConnectionFactory == null) + { + throw new ArgumentException( + "'TargetConnectionFactory' is required for lazily initializing a Connection"); + } + lock (connectionMonitor) + { + if (this.target != null) + { + CloseConnection(this.target); + } + this.target = DoCreateConnection(); + PrepareConnection(this.target); + if (LOG.IsDebugEnabled) + { + LOG.Info("Established shared NMS Connection: " + this.target); + } + this.connection = GetSharedConnection(this.target); + } + } + + protected virtual void PrepareConnection(IConnection con) + { + if (ClientId != null) + { + con.ClientId = ClientId; + } + if (ExceptionListenerDelegate != null || ReconnectOnException) + { + ExceptionListener listenerToUse = ExceptionListenerDelegate; + if (ReconnectOnException) + { + InternalChainedExceptionListenerSupport chained = new InternalChainedExceptionListenerSupport(this, listenerToUse); + con.ExceptionListener += chained.OnException; + } + + } + } + + protected virtual IConnection DoCreateConnection() + { + return TargetConnectionFactory.CreateConnection(); + } + + private void CloseConnection(IConnection con) + { + try + { + try + { + con.Stop(); + } finally + { + con.Close(); + } + } catch (Exception ex) + { + LOG.Warn("Could not close shared NMS connection."); + } + } + + public IConnection CreateConnection(string userName, string password) + { + throw new NotImplementedException(); + } + + #endregion + + #region IInitializingObject Members + + public void AfterPropertiesSet() + { + if (connection == null && TargetConnectionFactory == null) + { + throw new ArgumentException("Connection or 'TargetConnectionFactory' is required."); + } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + ResetConnection(); + } + + public void ResetConnection() + { + lock (connectionMonitor) + { + if (this.target != null) + { + CloseConnection(this.target); + } + this.target = null; + this.connection = null; + } + } + + #endregion + + protected virtual IConnection GetSharedConnection(IConnection target) + { + lock (connectionMonitor) + { + return new CloseSupressingConnection(target); + } + } + } + + internal class InternalChainedExceptionListenerSupport + { + private SingleConnectionFactory factory; + private ExceptionListener listenerToUse; + public InternalChainedExceptionListenerSupport(SingleConnectionFactory factory, ExceptionListener listenerToUse) + { + this.factory = factory; + this.listenerToUse = listenerToUse; + } + + public void OnException(Exception exception) + { + //TODO exception mgmt. + } + } + + internal class CloseSupressingConnection : IConnection + { + private IConnection target; + + public CloseSupressingConnection(IConnection target) + { + this.target = target; + } + + public event ExceptionListener ExceptionListener + { + add { target.ExceptionListener += value; } + remove { target.ExceptionListener -= value; } + } + + public ISession CreateSession() + { + return target.CreateSession(); + } + + public ISession CreateSession(AcknowledgementMode acknowledgementMode) + { + return target.CreateSession(acknowledgementMode); + } + + public void Close() + { + // don't pass the call to the target. + } + + public AcknowledgementMode AcknowledgementMode + { + get { return target.AcknowledgementMode; } + set { target.AcknowledgementMode = value; } + } + + public string ClientId + { + get { return target.ClientId; } + set { target.ClientId = value; } + } + + public void Dispose() + { + target.Dispose(); + } + + public void Start() + { + target.Start(); + } + + public bool IsStarted + { + get { return target.IsStarted; } + } + + public void Stop() + { + //don't pass the call to the target. + } + } +} \ No newline at end of file diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs index 238372e4..2a645f77 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractMessageListenerContainer.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using Spring.Context; +using Spring.Messaging.Nms.IConnections; using Spring.Messaging.Nms.Support; using Spring.Messaging.Nms.Support.IDestinations; using Spring.Util; @@ -505,7 +506,7 @@ namespace Spring.Messaging.Nms.Listener { lock (this.sharedConnectionMonitor) { - NmsUtils.CloseConnection(this.sharedConnection); + ConnectionFactoryUtils.ReleaseConnection(sharedConnection, ConnectionFactory, autoStartup); } throw; } diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs index 33b7c917..9b6afca8 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/NmsTemplate.cs @@ -77,8 +77,6 @@ namespace Spring.Messaging.Nms private TimeSpan timeToLive; - private bool cacheNmsResources = true; - private NmsResources jmsResources = new NmsResources(); #endregion @@ -180,17 +178,10 @@ namespace Spring.Messaging.Nms return action.DoInNms(sessionToUse); } //TODO make sure don't want to do exception translation. - //TODO investigate options to not close session/connection via caching since - //these objects are thread safe in tibco ems. finally { - if (!CacheNmsResources) - { - NmsUtils.CloseSession(sessionToClose); - NmsUtils.CloseConnection(conToClose, startConnection); - } - //TODO No IConnectionFactory interface so can't create - // non closing impl of IConnection Need NMS. + NmsUtils.CloseSession(sessionToClose); + ConnectionFactoryUtils.ReleaseConnection(conToClose, ConnectionFactory, startConnection); } } @@ -358,12 +349,6 @@ namespace Spring.Messaging.Nms } - virtual public bool CacheNmsResources - { - get { return cacheNmsResources; } - set { cacheNmsResources = value; } - } - #endregion protected virtual object DoConvertFromMessage(IMessage message) @@ -401,58 +386,6 @@ namespace Spring.Messaging.Nms return holder.GetSession(); } - /// Create a NMS IConnection via this template's IConnectionFactory. - /// - /// If CacheNmsResource is true, then the connection - /// will be created upon the first invocation and will retrun the same - /// connection on all subsequent calls. - /// - /// A NMS IConnection - /// - /// NMSException if thrown by NMS API methods - protected virtual IConnection CreateConnection() - { - if (this.CacheNmsResources) - { - if (jmsResources.Connection == null) - { - jmsResources.Connection = ConnectionFactory.CreateConnection(); - } - return jmsResources.Connection; - - } - else - { - return ConnectionFactory.CreateConnection(); - } - - } - - /// Create a NMS ISession for the given IConnection. - ///

This implementation uses NMS 1.1 API.

- ///
- /// the NMS IConnection to create a ISession for - /// - /// the new NMS ISession - /// - /// NMSException if thrown by NMS API methods - protected virtual ISession CreateSession(IConnection con) - { - if (CacheNmsResources) - { - if (jmsResources.Session == null) - { - jmsResources.Session = jmsResources.Connection.CreateSession(SessionAcknowledgeMode); - } - return jmsResources.Session; - } - else - { - return con.CreateSession(SessionAcknowledgeMode); - } - } - - /// Create a NMS IMessageProducer for the given ISession and IDestination, /// configuring it to disable message ids and/or timestamps (if necessary). ///

Delegates to doCreateProducer for creation of the raw @@ -498,18 +431,7 @@ namespace Spring.Messaging.Nms /// NMSException if thrown by NMS API methods protected virtual IMessageProducer DoCreateProducer(ISession session, IDestination destination) { - if (CacheNmsResources) - { - if (jmsResources.MessageProducer == null) - { - jmsResources.MessageProducer = session.CreateProducer(destination); - } - return jmsResources.MessageProducer; - } - else - { - return session.CreateProducer(destination); - } + return session.CreateProducer(destination); } ///

Create a NMS IMessageConsumer for the given ISession and IDestination. @@ -595,19 +517,12 @@ namespace Spring.Messaging.Nms } } finally - { - CloseProducer(producer); - } - } - - private void CloseProducer(IMessageProducer producer) - { - if (!CacheNmsResources) { NmsUtils.CloseMessageProducer(producer); } } + /// Actually send the given NMS message. /// the NMS IMessageProducer to send with /// diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs index 29b816a8..fe17bd6c 100644 --- a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Support/NmsAccessor.cs @@ -112,8 +112,14 @@ namespace Spring.Messaging.Nms.Support /// public bool SessionTransacted { - get { return sessionTransacted; } - set { sessionTransacted = value; } + get { return SessionAcknowledgeMode == AcknowledgementMode.Transactional; } + set + { + if (value) + { + sessionAcknowledgeMode = AcknowledgementMode.Transactional; + } + } } #endregion @@ -126,5 +132,19 @@ namespace Spring.Messaging.Nms.Support throw new ArgumentException("ConnectionFactory is required"); } } + + /// + /// Creates the connection via the ConnectionFactory. + /// + /// + protected virtual IConnection CreateConnection() + { + return ConnectionFactory.CreateConnection(); + } + + protected virtual ISession CreateSession(IConnection con) + { + return con.CreateSession(SessionAcknowledgeMode); + } } } diff --git a/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj index 8c56ff7c..e61716dd 100644 --- a/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj +++ b/src/Spring/Spring.Messaging.Nms/Spring.Messaging.Nms.2005.csproj @@ -48,7 +48,10 @@ + + + @@ -74,6 +77,10 @@ + + {3A3A4E65-45A6-4B20-B460-0BEDC302C02C} + Spring.Aop.2005 + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} Spring.Core.2005 diff --git a/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTests.cs b/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTests.cs index 447233e7..80959118 100644 --- a/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTests.cs +++ b/test/Spring/Spring.Data.Integration.Tests/Data/NativeAdoTests.cs @@ -53,6 +53,7 @@ namespace Spring.Data SqlConnection conn = new SqlConnection(connString); conn.Open(); + conn.Open(); //conn.BeginTransaction(IsolationLevel.Unspecified); SqlTransaction trans = conn.BeginTransaction(); Console.WriteLine(trans.IsolationLevel); diff --git a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.dll.config b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.dll.config index 12e5ecbf..63079081 100644 --- a/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.dll.config +++ b/test/Spring/Spring.Data.NHibernate.Integration.Tests/Spring.Data.NHibernate.Integration.Tests.dll.config @@ -4,9 +4,6 @@
- -
- diff --git a/test/Spring/Spring.Messaging.Nms.Tests/AssemblyInfo.cs b/test/Spring/Spring.Messaging.Nms.Tests/AssemblyInfo.cs index 177a4f0e..b81fdfc0 100644 --- a/test/Spring/Spring.Messaging.Nms.Tests/AssemblyInfo.cs +++ b/test/Spring/Spring.Messaging.Nms.Tests/AssemblyInfo.cs @@ -1,58 +1,5 @@ using System.Reflection; using System.Runtime.CompilerServices; -// -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -// -[assembly: AssemblyTitle("")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: - -[assembly: AssemblyVersion("1.0.*")] - -// -// In order to sign your assembly you must specify a key to use. Refer to the -// Microsoft .NET Framework documentation for more information on assembly signing. -// -// Use the attributes below to control which key is used for signing. -// -// Notes: -// (*) If no key is specified, the assembly is not signed. -// (*) KeyName refers to a key that has been installed in the Crypto Service -// Provider (CSP) on your machine. KeyFile refers to a file which contains -// a key. -// (*) If the KeyFile and the KeyName values are both specified, the -// following processing occurs: -// (1) If the KeyName can be found in the CSP, that key is used. -// (2) If the KeyName does not exist and the KeyFile does exist, the key -// in the KeyFile is installed into the CSP and used. -// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. -// When specifying the KeyFile, the location of the KeyFile should be -// relative to the project output directory which is -// %Project Directory%\obj\. For example, if your KeyFile is -// located in the project directory, you would specify the AssemblyKeyFile -// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] -// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework -// documentation for more information on this. -// -[assembly: AssemblyDelaySign(false)] -[assembly: AssemblyKeyFile("")] -[assembly: AssemblyKeyName("")] +[assembly: AssemblyTitle("Spring.Messaging.Nms Tests")] +[assembly: AssemblyDescription("Unit tests for Spring.Messaging.Nms assembly")] diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Connections/NmsTransactionManagerTests.cs b/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Connections/NmsTransactionManagerTests.cs new file mode 100644 index 00000000..6633e7e9 --- /dev/null +++ b/test/Spring/Spring.Messaging.Nms.Tests/Messaging/Nms/Connections/NmsTransactionManagerTests.cs @@ -0,0 +1,338 @@ +#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 Apache.NMS; +using NUnit.Framework; +using Rhino.Mocks; +using Spring.Messaging.Nms.IConnections; +using Spring.Transaction; +using Spring.Transaction.Support; + +#endregion + +namespace Spring.Messaging.Nms.Connections +{ + /// + /// This class contains tests for + /// + /// Mark Pollack + /// $Id:$ + [TestFixture] + public class NmsTransactionManagerTests + { + private MockRepository mocks; + + [SetUp] + public void Setup() + { + mocks = new MockRepository(); + } + + [Test] + public void TransactionCommit() + { + IConnectionFactory connectionFactory = (IConnectionFactory) mocks.CreateMock(typeof (IConnectionFactory)); + IConnection connection = (IConnection) mocks.CreateMock(typeof (IConnection)); + ISession session = (ISession) mocks.CreateMock(typeof (ISession)); + + using (mocks.Ordered()) + { + SetupCommitExpectations(connection, connectionFactory, session); + } + + mocks.ReplayAll(); + + NmsTransactionManager tm = new NmsTransactionManager(connectionFactory); + ITransactionStatus ts = tm.GetTransaction(new DefaultTransactionDefinition()); + NmsTemplate nt = new NmsTemplate(connectionFactory); + nt.Execute(new AssertSessionCallback(session)); + tm.Commit(ts); + + mocks.VerifyAll(); + + } + + [Test] + public void TransactionRollback() + { + IConnectionFactory connectionFactory = (IConnectionFactory) mocks.CreateMock(typeof (IConnectionFactory)); + IConnection connection = (IConnection) mocks.CreateMock(typeof (IConnection)); + ISession session = (ISession) mocks.CreateMock(typeof (ISession)); + + SetupRollbackExpectations(connection, connectionFactory, session); + + mocks.ReplayAll(); + + NmsTransactionManager tm = new NmsTransactionManager(connectionFactory); + ITransactionStatus ts = tm.GetTransaction(new DefaultTransactionDefinition()); + NmsTemplate nt = new NmsTemplate(connectionFactory); + nt.Execute(new AssertSessionCallback(session)); + tm.Rollback(ts); + + mocks.VerifyAll(); + } + + /** + * TODO because using anonymous delegates - refactor to support .net 1.1 later. + */ +#if NET_2_0 + [Test] + public void ParticipatingTransactionWithCommit() + { + IConnectionFactory connectionFactory = (IConnectionFactory)mocks.CreateMock(typeof(IConnectionFactory)); + IConnection connection = (IConnection)mocks.CreateMock(typeof(IConnection)); + ISession session = (ISession)mocks.CreateMock(typeof(ISession)); + + using (mocks.Ordered()) + { + SetupCommitExpectations(connection, connectionFactory, session); + } + + mocks.ReplayAll(); + + + NmsTransactionManager tm = new NmsTransactionManager(connectionFactory); + ITransactionStatus ts = tm.GetTransaction(new DefaultTransactionDefinition()); + NmsTemplate nt = new NmsTemplate(connectionFactory); + nt.Execute(new AssertSessionCallback(session)); + + TransactionTemplate tt = new TransactionTemplate(tm); + tt.Execute(delegate(ITransactionStatus status) + { + nt.Execute(new AssertSessionCallback(session)); + return null; + }); + + tm.Commit(ts); + + mocks.VerifyAll(); + + } + + [Test] + public void ParticipatingTransactionWithRollback() + { + IConnectionFactory connectionFactory = (IConnectionFactory) mocks.CreateMock(typeof (IConnectionFactory)); + IConnection connection = (IConnection) mocks.CreateMock(typeof (IConnection)); + ISession session = (ISession) mocks.CreateMock(typeof (ISession)); + + using (mocks.Ordered()) + { + SetupRollbackExpectations(connection, connectionFactory, session); + } + + mocks.ReplayAll(); + + NmsTransactionManager tm = new NmsTransactionManager(connectionFactory); + ITransactionStatus ts = tm.GetTransaction(new DefaultTransactionDefinition()); + NmsTemplate nt = new NmsTemplate(connectionFactory); + nt.Execute(new AssertSessionCallback(session)); + + TransactionTemplate tt = new TransactionTemplate(tm); + tt.Execute(delegate(ITransactionStatus status) + { + nt.Execute(new AssertSessionCallback(session)); + status.RollbackOnly = true; + return null; + }); + try + { + tm.Commit(ts); + Assert.Fail("Should have thrown UnexpectedRollbackException"); + } catch (UnexpectedRollbackException) + { + + } + + mocks.VerifyAll(); + } + + [Test] + public void SuspendedTransaction() + { + IConnectionFactory connectionFactory = (IConnectionFactory) mocks.CreateMock(typeof (IConnectionFactory)); + IConnection connection = (IConnection) mocks.CreateMock(typeof (IConnection)); + ISession session = (ISession) mocks.CreateMock(typeof (ISession)); + ISession session2 = (ISession)mocks.CreateMock(typeof(ISession)); + + Expect.Call(connectionFactory.CreateConnection()).Return(connection).Repeat.Twice(); + Expect.Call(connection.CreateSession(AcknowledgementMode.Transactional)).Return(session).Repeat.Once(); + Expect.Call(connection.CreateSession(AcknowledgementMode.AutoAcknowledge)).Return(session2).Repeat.Once(); + + session.Commit(); + LastCall.On(session).Repeat.Once(); + session.Close(); + LastCall.On(session).Repeat.Once(); + + session2.Close(); + LastCall.On(session2).Repeat.Once(); + + connection.Close(); + LastCall.On(connection).Repeat.Twice(); + + mocks.ReplayAll(); + + NmsTransactionManager tm = new NmsTransactionManager(connectionFactory); + ITransactionStatus ts = tm.GetTransaction(new DefaultTransactionDefinition()); + NmsTemplate nt = new NmsTemplate(connectionFactory); + nt.Execute(new AssertSessionCallback(session)); + + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.NotSupported; + tt.Execute(delegate(ITransactionStatus status) + { + nt.Execute(new AssertNotSameSessionCallback(session)); + return null; + }); + + nt.Execute(new AssertSessionCallback(session)); + + tm.Commit(ts); + + mocks.VerifyAll(); + + } + + [Test] + public void TransactionSuspension() + { + IConnectionFactory connectionFactory = (IConnectionFactory)mocks.CreateMock(typeof(IConnectionFactory)); + IConnection connection = (IConnection)mocks.CreateMock(typeof(IConnection)); + ISession session = (ISession)mocks.CreateMock(typeof(ISession)); + ISession session2 = (ISession)mocks.CreateMock(typeof(ISession)); + + + Expect.Call(connectionFactory.CreateConnection()).Return(connection).Repeat.Twice(); + Expect.Call(connection.CreateSession(AcknowledgementMode.Transactional)).Return(session).Repeat.Once(); + Expect.Call(connection.CreateSession(AcknowledgementMode.Transactional)).Return(session2).Repeat.Once(); + + session.Commit(); + LastCall.On(session).Repeat.Once(); + session2.Commit(); + LastCall.On(session2).Repeat.Once(); + + session.Close(); + LastCall.On(session).Repeat.Once(); + session2.Close(); + LastCall.On(session2).Repeat.Once(); + + connection.Close(); + LastCall.On(connection).Repeat.Twice(); + + mocks.ReplayAll(); + + NmsTransactionManager tm = new NmsTransactionManager(connectionFactory); + ITransactionStatus ts = tm.GetTransaction(new DefaultTransactionDefinition()); + NmsTemplate nt = new NmsTemplate(connectionFactory); + + + TransactionTemplate tt = new TransactionTemplate(tm); + tt.PropagationBehavior = TransactionPropagation.RequiresNew; + tt.Execute(delegate(ITransactionStatus status) + { + nt.Execute(new AssertNotSameSessionCallback(session)); + return null; + }); + + nt.Execute(new AssertSessionCallback(session)); + + tm.Commit(ts); + + mocks.VerifyAll(); + + } +#endif + + private static void SetupRollbackExpectations(IConnection connection, IConnectionFactory connectionFactory, ISession session) + { + Expect.Call(connectionFactory.CreateConnection()).Return(connection).Repeat.Once(); + Expect.Call(connection.CreateSession(AcknowledgementMode.Transactional)).Return(session).Repeat.Once(); + session.Rollback(); + LastCall.On(session).Repeat.Once(); + session.Close(); + LastCall.On(session).Repeat.Once(); + connection.Close(); + LastCall.On(connection).Repeat.Once(); + } + + private static void SetupCommitExpectations(IConnection connection, IConnectionFactory connectionFactory, ISession session) + { + Expect.Call(connectionFactory.CreateConnection()).Return(connection).Repeat.Once(); + Expect.Call(connection.CreateSession(AcknowledgementMode.Transactional)).Return(session).Repeat.Once(); + session.Commit(); + LastCall.On(session).Repeat.Once(); + session.Close(); + LastCall.On(session).Repeat.Once(); + connection.Close(); + LastCall.On(connection).Repeat.Once(); + } + + [TearDown] + public void TearDown() + { + Assert.IsTrue(TransactionSynchronizationManager.ResourceDictionary.Count == 0); + Assert.IsFalse(TransactionSynchronizationManager.SynchronizationActive); + + } + + + } + + internal class AssertSessionCallback : ISessionCallback + { + private ISession session; + public AssertSessionCallback(ISession session) + { + this.session = session; + } + + #region ISessionCallback Members + + public object DoInNms(ISession session) + { + Assert.IsTrue(this.session == session); + return null; + } + + #endregion + } + + internal class AssertNotSameSessionCallback : ISessionCallback + { + private ISession session; + public AssertNotSameSessionCallback(ISession session) + { + this.session = session; + } + + #region ISessionCallback Members + + public object DoInNms(ISession session) + { + Assert.IsTrue(this.session != session); + return null; + } + + #endregion + } +} \ No newline at end of file diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2005.csproj b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2005.csproj index fed9eaac..7c5acc53 100644 --- a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2005.csproj +++ b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.2005.csproj @@ -15,7 +15,7 @@ full false ..\..\..\build\VS.NET.2005\Spring.Messaging.Nms.Tests\Debug\ - DEBUG;TRACE + TRACE;DEBUG;NET_2_0 prompt 4 @@ -28,24 +28,61 @@ 4 + False ..\..\..\lib\net\2.0\antlr.runtime.dll + + False + ..\..\..\lib\Net\2.0\Apache.NMS.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.dll + + + False + ..\..\..\lib\Net\2.0\Common.Logging.Log4Net.dll + + + False + ..\..\..\lib\Net\2.0\log4net.dll + False ..\..\..\lib\net\2.0\nunit.framework.dll + + False + ..\..\..\lib\Net\2.0\Rhino.Mocks.dll + + + {710961A3-0DF4-49E4-A26E-F5B9C044AC84} + Spring.Core.2005 + + + {AE00E5AB-C39A-436F-86D2-33BFE33E2E40} + Spring.Data.2005 + {AEB1578C-9018-4D49-B440-789F38DD2F29} Spring.Messaging.Nms.2005 + + + + + + Always + + diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.build b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.build index 12cdae9b..293830e7 100644 --- a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.build +++ b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.build @@ -20,7 +20,7 @@ - + diff --git a/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.dll.config b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.dll.config new file mode 100644 index 00000000..98ee661f --- /dev/null +++ b/test/Spring/Spring.Messaging.Nms.Tests/Spring.Messaging.Nms.Tests.dll.config @@ -0,0 +1,25 @@ + + + + + + + + + + + diff --git a/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2005.csproj b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2005.csproj index 6cd494bb..765a7fa6 100644 --- a/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2005.csproj +++ b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2005.csproj @@ -192,7 +192,7 @@ echo "Copying .xml files for tests" -xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Web.Tests\$(ConfigurationName)\ /y /s /q /d +%25SystemRoot%25\system32\xcopy "$(ProjectDir)Data" ..\..\..\..\build\VS.Net.2005\Spring.Web.Tests\$(ConfigurationName)\ /y /s /q /d \ No newline at end of file