Revision: 114
http://svn.sourceforge.net/nmailserver/?rev=114&view=rev
Author: tmyroadctfig
Date: 2007-01-10 02:19:39 -0800 (Wed, 10 Jan 2007)
Log Message:
-----------
Added missing base classes.
Added Paths:
-----------
NMail/trunk/NMail/DataTypes/Service/BaseService.cs
NMail/trunk/NMail/DataTypes/Service/BaseSession.cs
Added: NMail/trunk/NMail/DataTypes/Service/BaseService.cs
===================================================================
--- NMail/trunk/NMail/DataTypes/Service/BaseService.cs (rev 0)
+++ NMail/trunk/NMail/DataTypes/Service/BaseService.cs 2007-01-10 10:19:39 UTC (rev 114)
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2004-2006 Luke Quinane
+ *
+ * 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.
+ *
+ */
+
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+
+using log4net;
+
+using NMail.DataTypes;
+using NMail.Configuration;
+using NMail.Helper;
+
+namespace NMail.DataTypes.Service {
+ /// <summary>
+ /// Provides a base class for services.
+ /// </summary>
+ public abstract class BaseService<SessionType> : MarshalByRefObject, IService where SessionType : BaseSession {
+ /// <summary>
+ /// A list of sockets listening for new connections.
+ /// </summary>
+ private List<TcpListener> listenSockets;
+
+ /// <summary>
+ /// A list of threads processing current sessions.
+ /// </summary>
+ private List<Thread> sessionThreads;
+
+ /// <summary>
+ /// A flag indicating the server is shutting down or shutdown.
+ /// </summary>
+ private bool shutdown;
+
+ /// <summary>
+ /// The performance counter for tracking the number of received connections.
+ /// </summary>
+ private PerformanceCounter connectionsReceived;
+
+ /// <summary>
+ /// Creates a new instance of the SMTP service.
+ /// </summary>
+ public BaseService() {
+ this.listenSockets = new List<TcpListener>();
+ this.sessionThreads = new List<Thread>();
+ this.shutdown = true;
+
+ this.connectionsReceived = new PerformanceCounter(
+ PerfCounterCategory,
+ TotalConnectionsReceived,
+ false);
+ }
+
+ #region IService Members
+ public void Start() {
+ this.shutdown = false;
+
+ // Create a new thread to listen on each listen end point
+ foreach (IPEndPointElement endPointElement in this.ListenEndpoints) {
+ ThreadHelper th = new ThreadHelper(new WaitCallback(handleConnections),
+ endPointElement.ToEndPoint());
+
+ Thread listenThread = new Thread(new ThreadStart(th.CallDelegate));
+ listenThread.Start();
+ }
+
+ // Notify any listeners that the service has started
+ if (this.startedEvent != null) {
+ this.startedEvent(this, new EventArgs());
+ }
+ }
+
+ public void Init() { }
+
+ public abstract string Name { get; }
+
+ public abstract string Description { get; }
+
+ public IService ReloadConfiguration() {
+ return this;
+ }
+
+ public bool AllowsReloadConfiguration {
+ get {
+ return false;
+ }
+ }
+
+ public void Stop(bool runCurrentToCompletion) {
+ this.shutdown = true;
+
+ lock (this) {
+ // Kill each of the listening sockets
+ foreach (TcpListener listener in this.listenSockets) {
+ listener.Stop();
+ }
+ this.listenSockets.Clear();
+ }
+
+ if (runCurrentToCompletion) {
+ // wait for sessions threads to finish up
+ while (this.sessionThreads.Count > 0) {
+ Thread.Sleep(500);
+ }
+ } else {
+ lock (this) {
+ // non-graceful shutdown of smtp sessions
+ foreach (Thread thread in this.sessionThreads) {
+ thread.Abort();
+ }
+ this.sessionThreads.Clear();
+ }
+ }
+
+ // Notify any listeners that the service has stopped
+ if (this.stoppedEvent != null) {
+ this.stoppedEvent(this, new EventArgs());
+ }
+ }
+
+ public bool SupportsPause {
+ get {
+ return false;
+ }
+ }
+
+ public void Pause() {
+ throw new InvalidOperationException("Pause not supported.");
+ }
+
+ public void Continue() {
+ throw new InvalidOperationException("Pause not supported.");
+ }
+
+ public abstract Uri SupportUrl { get; }
+
+ public bool Running {
+ get {
+ return ((this.listenSockets.Count > 0) || (this.sessionThreads.Count > 0));
+ }
+ }
+
+ private event EventHandler startedEvent;
+
+ public event EventHandler Started {
+ add {
+ this.startedEvent += value;
+ }
+ remove {
+ this.startedEvent -= value;
+ }
+ }
+
+ private event EventHandler stoppedEvent;
+
+ public event EventHandler Stopped {
+ add {
+ this.stoppedEvent += value;
+ }
+ remove {
+ this.stoppedEvent -= value;
+ }
+ }
+ #endregion
+
+ public int ConnectionCount {
+ get {
+ return this.sessionThreads.Count;
+ }
+ }
+
+ /// <summary>
+ /// Handles incomming connections for an interface (IP address).
+ /// </summary>
+ /// <param name="o">The interface to listen for connections on.</param>
+ protected void handleConnections(object o) {
+ Thread.CurrentThread.Name = "HandleConnections:" + o.ToString();
+ TcpListener socket = null;
+
+ try {
+ IPEndPoint ep = (IPEndPoint) o;
+ if (Log.IsDebugEnabled) {
+ Log.Debug("Binding to IP endpoint: [" + ep + "]");
+ }
+ socket = new TcpListener(ep);
+ socket.Start();
+ lock (this) {
+ // Register this socket
+ this.listenSockets.Add(socket);
+ }
+
+ while (true) {
+ Socket client = socket.AcceptSocket();
+ this.connectionsReceived.Increment();
+
+ lock (this) {
+ if (this.ConnectionCount < this.MaximumConnections) {
+ // Create a new session and thread to handle this connection
+ SessionType session = CreateSession(client);
+ Thread sessionThread = new Thread(new ThreadStart(session.Process));
+ this.RegisterSession(sessionThread);
+ sessionThread.Start();
+ } else {
+ Log.Warn("Server too busy to accept connections.");
+ client.Send(Encoding.ASCII.GetBytes(BusyReply));
+ client.Close();
+ }
+ }
+ }
+ } catch (SocketException e) {
+ // Ignore interrupted exception during shutdown
+ if (!this.shutdown) {
+ Log.Warn(e.Message, e);
+ }
+
+ lock (this) {
+ // De-register this socket
+ this.listenSockets.Remove(socket);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Registers the session thread with the service so that it can be kill if
+ /// required.
+ /// </summary>
+ /// <param name="sessionThread">The session thread to register.</param>
+ public void RegisterSession(Thread sessionThread) {
+ lock (this) {
+ this.sessionThreads.Add(sessionThread);
+ }
+ }
+
+ /// <summary>
+ /// Deregisters the session thread with the service. The session thread will not
+ /// attempt to kill this thread in a shutdown.
+ /// </summary>
+ /// <param name="sessionThread">The session thread to deregister.</param>
+ public void DeregisterSession(Thread sessionThread) {
+ lock (this) {
+ this.sessionThreads.Remove(sessionThread);
+ }
+ }
+
+ /// <summary>
+ /// Creates a new session for the given client socket.
+ /// </summary>
+ /// <param name="client">The socket to use in the session.</param>
+ /// <returns>The session.</returns>
+ protected abstract SessionType CreateSession(Socket client);
+
+ /// <summary>
+ /// The log for this service.
+ /// </summary>
+ protected abstract ILog Log { get; }
+
+ /// <summary>
+ /// The maximum number of connections allowed.
+ /// </summary>
+ protected abstract int MaximumConnections { get; }
+
+ /// <summary>
+ /// The listen endpoints for this service.
+ /// </summary>
+ protected abstract IPEndPointsCollection ListenEndpoints { get; }
+
+ /// <summary>
+ /// The reply to send when the server is busy.
+ /// </summary>
+ protected abstract string BusyReply { get; }
+
+ /// <summary>
+ /// The category the SMTP service uses for its performance counters.
+ /// </summary>
+ public abstract string PerfCounterCategory { get; }
+
+ /// <summary>
+ /// The name of the performance counter that tracks the total number of
+ /// received connections.
+ /// </summary>
+ public const string TotalConnectionsReceived = "TotalConnectionsReceived";
+ }
+}
\ No newline at end of file
Added: NMail/trunk/NMail/DataTypes/Service/BaseSession.cs
===================================================================
--- NMail/trunk/NMail/DataTypes/Service/BaseSession.cs (rev 0)
+++ NMail/trunk/NMail/DataTypes/Service/BaseSession.cs 2007-01-10 10:19:39 UTC (rev 114)
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2004-2006 Luke Quinane
+ *
+ * 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.
+ *
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Net.Sockets;
+using System.Text;
+
+namespace NMail.DataTypes.Service {
+ /// <summary>
+ /// The base class for a session.
+ /// </summary>
+ public abstract class BaseSession {
+
+ public BaseSession(Socket socket) {
+ this.socket = socket;
+ }
+
+ protected Socket socket;
+
+ /// <summary>
+ /// The socket to the client.
+ /// </summary>
+ protected Socket Socket {
+ get { return socket; }
+ }
+
+ /// <summary>
+ /// Processes the current session.
+ /// </summary>
+ public abstract void Process();
+ }
+}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|