diff --git a/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractNmsListeningContainer.cs b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractNmsListeningContainer.cs new file mode 100644 index 00000000..a0ab6e28 --- /dev/null +++ b/src/Spring/Spring.Messaging.Nms/Messaging/Nms/Listener/AbstractNmsListeningContainer.cs @@ -0,0 +1,345 @@ +#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 Spring.Context; +using Spring.Messaging.Nms.Connection; +using Spring.Messaging.Nms.Support; +using Spring.Messaging.Nms.Support.IDestinations; +using Spring.Objects.Factory; + +namespace Spring.Messaging.Nms.Listener +{ + /// + /// Common base class for all containers which need to implement listening + /// based on a Connection (either shared or freshly obtained for each attempt). + /// Inherits basic Connection and Session configuration handling from the + /// base class. + /// + /// + /// This class provides basic lifecycle management, in particular management + /// of a shared Connection. Subclasses are supposed to plug into this + /// lifecycle, implementing the as well as + /// + /// + /// + /// + /// + /// Mark Pollack + public abstract class AbstractNmsListeningContainer : NmsDestinationAccessor, ILifecycle, IObjectNameAware, IDisposable + { + #region Fields + + private String clientId; + + protected bool autoStartup = true; + + private string objectName; + + private IConnection sharedConnection; + + protected object sharedConnectionMonitor = new object(); + + private volatile bool active = false; + + private bool running = false; + + protected object lifecycleMonitor = new object(); + + #endregion + + public string ClientId + { + set { clientId = value; } + get { return clientId; } + } + + /// Set whether to automatically start the listener after initialization. + ///

Default is "true"; set this to "false" to allow for manual startup.

+ ///
+ public virtual bool AutoStartup + { + set { this.autoStartup = value; } + } + + public bool IsRunning + { + get + { + lock (lifecycleMonitor) + { + return running; + } + } + } + + /// Return whether a shared NMS IConnection should be maintained + /// by this listener container base class. + /// + /// + /// + protected abstract bool SharedConnectionEnabled { get; } + + public void Dispose() + { + Shutdown(); + } + + virtual public bool Active + { + get + { + lock (this.lifecycleMonitor) + { + return this.active; + } + } + + } + + public string ObjectName + { + set { objectName = value; } + } + + + public void Start() + { + DoStart(); + } + + public virtual void Shutdown() + { + logger.Debug("Shutting down message listener container"); + bool wasRunning = false; + lock (this.lifecycleMonitor) + { + wasRunning = this.running; + this.running = false; + this.active = false; + System.Threading.Monitor.PulseAll(this.lifecycleMonitor); + } + + if (wasRunning && SharedConnectionEnabled) + { + try + { + StopSharedConnection(); + } catch (Exception ex) + { + logger.Debug("Could not stop NMS Connection on shutdown", ex); + } + } + + // Shut down the invokers + try + { + DestroyListener(); + } + finally + { + lock (this.sharedConnectionMonitor) + { + NmsUtils.CloseConnection(this.sharedConnection, wasRunning); + } + } + } + + protected void DoStart() + { + lock (this.lifecycleMonitor) + { + running = true; + System.Threading.Monitor.PulseAll(this.lifecycleMonitor); + + //TODO - PausedTasks + } + + if (SharedConnectionEnabled) + { + StartSharedConnection(); + } + } + + protected virtual void StartSharedConnection() + { + lock (sharedConnectionMonitor) + { + if (sharedConnection != null) + { + try + { + sharedConnection.Start(); + } + catch (Exception ex) + { + logger.Debug("Ignoring IConnection start exception - assuming already started", ex); + } + } + } + } + + public IConnection SharedConnection + { + get + { + if (!SharedConnectionEnabled) + { + throw new System.SystemException("This message listener container does not maintain a shared IConnection"); + } + lock (this.sharedConnectionMonitor) + { + if (this.sharedConnection == null) + { + //TODO SharedConnectionNotInitializedException + throw new ApplicationException("This message listener container's shared IConnection has not been initialized yet"); + } + return this.sharedConnection; + } + } + } + + public virtual void Initialize() + { + try + { + lock (this.lifecycleMonitor) + { + this.active = true; + System.Threading.Monitor.PulseAll(this.lifecycleMonitor); + } + + if (SharedConnectionEnabled) + { + EstablishSharedConnection(); + } + + if (this.autoStartup) + { + DoStart(); + } + + RegisterListener(); + } + catch (Exception) + { + lock (this.sharedConnectionMonitor) + { + ConnectionFactoryUtils.ReleaseConnection(sharedConnection, ConnectionFactory, autoStartup); + } + throw; + } + } + + protected virtual void EstablishSharedConnection() + { + RefreshSharedConnection(); + } + + + protected void RefreshSharedConnection() + { + bool running = IsRunning; + lock (this.sharedConnectionMonitor) + { + NmsUtils.CloseConnection(this.sharedConnection, running); + + IConnection con = CreateConnection(); + try + { + PrepareSharedConnection(con); + } + catch (Exception) + { + NmsUtils.CloseConnection(con); + throw; + } + this.sharedConnection = con; + } + } + + protected virtual void PrepareSharedConnection(IConnection connection) + { + if (ClientId != null) + { + connection.ClientId = ClientId; + } + } + + /// Register the specified listener on the underlying NMS IConnection. + ///

Subclasses need to implement this method for their specific + /// listener management process.

+ ///
+ /// NMSException if registration failed + /// + /// + /// + /// + protected abstract void RegisterListener(); + + public void Stop() + { + DoStop(); + } + + protected virtual void DoStop() + { + lock (this.lifecycleMonitor) + { + this.running = false; + System.Threading.Monitor.PulseAll(this.lifecycleMonitor); + } + + if (SharedConnectionEnabled) + { + StopSharedConnection(); + } + } + + protected virtual void StopSharedConnection() + { + lock (this.sharedConnectionMonitor) + { + if (this.sharedConnection != null) + { + try + { + this.sharedConnection.Stop(); + } + catch (System.InvalidOperationException ex) + { + logger.Debug("Ignoring IConnection stop exception - assuming already stopped", ex); + } + } + } + } + + + + /// Destroy the registered listener. + /// The NMS IConnection will automatically be closed afterwards + ///

Subclasses need to implement this method for their specific + /// listener management process.

+ ///
+ /// NMSException if destruction failed + protected abstract void DestroyListener(); + } +} \ No newline at end of file