
org.subethamail.smtp.server.SMTPServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of subethasmtp-smtp Show documentation
Show all versions of subethasmtp-smtp Show documentation
A multithreaded standalone SMTP mail server implementation.
The newest version!
package org.subethamail.smtp.server;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.subethamail.smtp.MessageHandlerFactory;
import org.subethamail.smtp.MessageListener;
import org.subethamail.smtp.Version;
/**
* Main SMTPServer class. Construct this object, set the
* hostName, port, and bind address if you wish to override the
* defaults, and call start().
*
* This class starts opens a ServerSocket and creates a new
* instance of the ConnectionHandler class when a new connection
* comes in. The ConnectionHandler then parses the incoming SMTP
* stream and hands off the processing to the CommandHandler which
* will execute the appropriate SMTP command class.
*
* This class also manages a watchdog thread which will timeout
* stale connections.
*
* There are two ways of using this server. The first is to
* construct with a MessageHandlerFactory. This provides the
* lowest-level and most flexible access. The second way is
* to construct with a collection of MessageListeners. This
* is a higher, and sometimes more convenient level of abstraction.
*
* In neither case is the SMTP server (this library) responsible
* for deciding what recipients to accept or what to do with the
* incoming data. That is left to you.
*
* @author Jon Stevens
* @author Ian McFarland <[email protected]>
* @author Jeff Schnitzer
*/
@SuppressWarnings("serial")
public class SMTPServer implements Runnable
{
private static Log log = LogFactory.getLog(SMTPServer.class);
private InetAddress bindAddress = null; // default to all interfaces
private int port = 25; // default to 25
private String hostName; // defaults to a lookup of the local address
private int backlog = 50;
private MessageHandlerFactory messageHandlerFactory;
private CommandHandler commandHandler;
private ServerSocket serverSocket;
private boolean go = false;
private Thread serverThread;
private Watchdog watchdog;
private ThreadGroup connectionHanderGroup;
/**
* set a hard limit on the maximum number of connections this server will accept
* once we reach this limit, the server will gracefully reject new connections.
* Default is 1000.
*/
private int maxConnections = 1000;
/**
* The timeout for waiting for data on a connection is one minute: 1000 * 60 * 1
*/
private int connectionTimeout = 1000 * 60 * 1;
/**
* The maximal number of recipients that this server accepts per message delivery request.
*/
private int maxRecipients = 1000;
/**
* The primary constructor.
*/
public SMTPServer(MessageHandlerFactory handlerFactory)
{
this.messageHandlerFactory = handlerFactory;
try
{
this.hostName = InetAddress.getLocalHost().getCanonicalHostName();
}
catch (UnknownHostException e)
{
this.hostName = "localhost";
}
this.commandHandler = new CommandHandler();
this.connectionHanderGroup = new ThreadGroup(SMTPServer.class.getName() + " ConnectionHandler Group");
}
/**
* A convenience constructor that splits the smtp data among multiple listeners
* (and multiple recipients).
*/
public SMTPServer(Collection listeners)
{
this(new MessageListenerAdapter(listeners));
}
/** @return the host name that will be reported to SMTP clients */
public String getHostName()
{
return this.hostName;
}
/** The host name that will be reported to SMTP clients */
public void setHostName(String hostName)
{
this.hostName = hostName;
}
/** null means all interfaces */
public InetAddress getBindAddress()
{
return this.bindAddress;
}
/** null means all interfaces */
public void setBindAddress(InetAddress bindAddress)
{
this.bindAddress = bindAddress;
}
/** */
public int getPort()
{
return this.port;
}
public void setPort(int port)
{
this.port = port;
}
/**
* Is the server running after start() has been called?
*/
public boolean isRunning()
{
return this.go;
}
/**
* The backlog is the Socket backlog.
*
* The backlog argument must be a positive value greater than 0.
* If the value passed if equal or less than 0, then the default value will be assumed.
*
* @return the backlog
*/
public int getBacklog()
{
return this.backlog;
}
/**
* The backlog is the Socket backlog.
*
* The backlog argument must be a positive value greater than 0.
* If the value passed if equal or less than 0, then the default value will be assumed.
*/
public void setBacklog(int backlog)
{
this.backlog = backlog;
}
/**
* Call this method to get things rolling after instantiating the
* SMTPServer.
*/
public synchronized void start()
{
if (this.serverThread != null)
throw new IllegalStateException("SMTPServer already started");
// Create our server socket here.
try
{
this.serverSocket = createServerSocket();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
this.go = true;
this.serverThread = new Thread(this, SMTPServer.class.getName());
// daemon threads do not keep the program from quitting;
// user threads keep the program from quitting.
// We want the serverThread to keep the program from quitting
// this.serverThread.setDaemon(true);
// Now call the serverThread.run() method
this.serverThread.start();
this.watchdog = new Watchdog(this);
// We do not want the watchdog to keep the program from quitting
this.watchdog.setDaemon(true);
this.watchdog.start();
}
/**
* Shut things down gracefully.
*/
public synchronized void stop()
{
// don't accept any more connections
this.go = false;
// kill the listening thread
this.serverThread = null;
// stop the watchdog
if (this.watchdog != null)
{
this.watchdog.quit();
this.watchdog = null;
}
// Shut down any open connections.
shutDownOpenConnections();
// if the serverSocket is not null, force a socket close for good measure
try
{
if (this.serverSocket != null && !this.serverSocket.isClosed())
this.serverSocket.close();
}
catch (IOException e)
{
}
}
/**
* Grabs all ThreadGroup instances of ConnectionHander's and attempts to close the
* socket if it is still open.
*/
protected void shutDownOpenConnections()
{
Thread[] groupThreads = new Thread[maxConnections];
ThreadGroup connectionGroup = getConnectionGroup();
connectionGroup.enumerate(groupThreads);
for (int i=0; i= maxConnections);
}
public int getMaxConnections()
{
return this.maxConnections;
}
/**
* Set's the maximum number of connections this server instance will
* accept.
*
* @param maxConnections
*/
public void setMaxConnections(int maxConnections)
{
this.maxConnections = maxConnections;
}
public int getConnectionTimeout()
{
return this.connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout)
{
this.connectionTimeout = connectionTimeout;
}
public int getMaxRecipients()
{
return this.maxRecipients;
}
public void setMaxRecipients(int maxRecipients)
{
this.maxRecipients = maxRecipients;
}
/**
* A watchdog thread that makes sure that connections don't go stale. It
* prevents someone from opening up MAX_CONNECTIONS to the server and
* holding onto them for more than 1 minute.
*/
private class Watchdog extends Thread
{
private SMTPServer server;
private boolean run = true;
public Watchdog(SMTPServer server)
{
super(Watchdog.class.getName());
this.server = server;
setPriority(Thread.MAX_PRIORITY / 3);
}
public void quit()
{
this.run = false;
}
public void run()
{
while (this.run)
{
Thread[] groupThreads = new Thread[maxConnections];
ThreadGroup connectionGroup = this.server.getConnectionGroup();
connectionGroup.enumerate(groupThreads);
for (int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy