Index: java/org/apache/james/core/AbstractJamesHandler.java =================================================================== --- java/org/apache/james/core/AbstractJamesHandler.java (revision 468922) +++ java/org/apache/james/core/AbstractJamesHandler.java (working copy) @@ -28,6 +28,7 @@ import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; +import org.apache.james.imapserver.debug.SplitOutputStream; import org.apache.james.services.DNSServer; import org.apache.james.util.CRLFTerminatedReader; import org.apache.james.util.InternetPrintWriter; @@ -36,6 +37,8 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; @@ -114,6 +117,11 @@ * The DNSServer */ protected DNSServer dnsServer = null; + + /** + * Used for debug: if not null enable tcp stream dump. + */ + private String tcplogprefix = null; /** @@ -145,6 +153,10 @@ // to be ASCII inReader = new CRLFTerminatedReader(in, "ASCII"); outs = new BufferedOutputStream(socket.getOutputStream(), 1024); + // enable tcp dump for debug + if (tcplogprefix != null) { + outs = new SplitOutputStream(outs, new FileOutputStream(tcplogprefix+"out")); + } out = new InternetPrintWriter(outs, true); } catch (RuntimeException e) { StringBuffer exceptionBuffer = @@ -317,7 +329,7 @@ * * @param theWatchdog the watchdog */ - void setWatchdog(Watchdog theWatchdog) { + public void setWatchdog(Watchdog theWatchdog) { this.theWatchdog = theWatchdog; } @@ -413,4 +425,22 @@ } + /** + * If not null, this will enable dump to file for tpc connections + * + * @param streamDumpDir the dir + */ + public void setStreamDumpDir(String streamDumpDir) { + if (streamDumpDir != null) { + String streamdumpDir=streamDumpDir; + this.tcplogprefix = streamdumpDir+"/TCP-DUMP."+System.currentTimeMillis()+"."; + File logdir = new File(streamdumpDir); + if (!logdir.exists()) { + logdir.mkdir(); + } + } else { + this.tcplogprefix = null; + } + } + } Index: java/org/apache/james/core/AbstractJamesService.java =================================================================== --- java/org/apache/james/core/AbstractJamesService.java (revision 475939) +++ java/org/apache/james/core/AbstractJamesService.java (working copy) @@ -186,6 +186,11 @@ private boolean connPerIPConfigured = false; private int connPerIP = 0; + /** + * If not null, it will be used to dump the tcp commands for debugging purpose + */ + private String streamDumpDir = null; + public void setConnectionManager(JamesConnectionManager connectionManager) { this.connectionManager = connectionManager; } @@ -222,6 +227,12 @@ // sense. We should modify the config to get rid of it. // Keeping it for now to maintain backwards compatibility. super.configure(handlerConfiguration); + + + boolean streamdump=handlerConfiguration.getChild("streamdump").getAttributeAsBoolean("enabled", false); + String streamdumpDir=streamdump ? handlerConfiguration.getChild("streamdump").getAttribute("directory", null) : null; + setStreamDumpDir(streamdumpDir); + port = conf.getChild("port").getValueAsInteger(getDefaultPort()); @@ -339,6 +350,10 @@ } + protected void setStreamDumpDir(String streamdumpDir) { + this.streamDumpDir = streamdumpDir; + } + private void configureHelloName(Configuration handlerConfiguration) { StringBuffer infoBuffer; String hostName = null; @@ -610,6 +625,7 @@ Watchdog theWatchdog = theWatchdogFactory.getWatchdog(theHandler.getWatchdogTarget()); theHandler.setConfigurationData(getConfigurationData()); + theHandler.setStreamDumpDir(streamDumpDir); theHandler.setWatchdog(theWatchdog); return theHandler; } Index: java/org/apache/james/imapserver/ImapHandler.java =================================================================== --- java/org/apache/james/imapserver/ImapHandler.java (revision 475252) +++ java/org/apache/james/imapserver/ImapHandler.java (working copy) @@ -19,29 +19,14 @@ package org.apache.james.imapserver; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.net.Socket; - import org.apache.avalon.cornerstone.services.connection.ConnectionHandler; import org.apache.avalon.excalibur.pool.Poolable; -import org.apache.avalon.framework.activity.Disposable; -import org.apache.avalon.framework.logger.AbstractLogEnabled; -import org.apache.avalon.framework.logger.Logger; import org.apache.james.Constants; -import org.apache.james.imapserver.debug.CopyInputStream; -import org.apache.james.imapserver.debug.SplitOutputStream; +import org.apache.james.core.AbstractJamesHandler; import org.apache.james.services.User; -import org.apache.james.util.InternetPrintWriter; -import org.apache.james.util.watchdog.Watchdog; -import org.apache.james.util.watchdog.WatchdogTarget; + +import java.io.IOException; +import java.net.Socket; /** * The handler class for IMAP connections. @@ -49,11 +34,11 @@ * should probably be rewritten from scratch. */ public class ImapHandler - extends AbstractLogEnabled + extends AbstractJamesHandler implements ImapHandlerInterface, ConnectionHandler, Poolable, ImapConstants { - private String softwaretype = "JAMES IMAP4rev1 Server " + Constants.SOFTWARE_VERSION; + private String softwaretype = "JAMES "+VERSION+" Server " + Constants.SOFTWARE_VERSION; private final ImapRequestHandler requestHandler = new ImapRequestHandler(); private ImapSession session; @@ -61,47 +46,6 @@ * The per-service configuration data that applies to all handlers */ private ImapHandlerConfigurationData theConfigData; - - /** - * The thread executing this handler - */ - private Thread handlerThread; - - /** - * The TCP/IP socket over which the IMAP interaction - * is occurring - */ - private Socket socket; - - /** - * The reader associated with incoming characters. - */ - private BufferedReader in; - - /** - * The socket's input stream. - */ - private InputStream ins; - - /** - * The writer to which outgoing messages are written. - */ - private PrintWriter out; - - /** - * The socket's output stream - */ - private OutputStream outs; - - /** - * The watchdog being used by this handler to deal with idle timeouts. - */ - private Watchdog theWatchdog; - - /** - * The watchdog target that idles out this handler. - */ - private WatchdogTarget theWatchdogTarget = new IMAPWatchdogTarget(); private boolean handlerIsUp=false; @@ -106,13 +50,10 @@ private boolean handlerIsUp=false; /** - * @see org.apache.avalon.framework.logger.AbstractLogEnabled#enableLogging(org.apache.avalon.framework.logger.Logger) + * The session termination status */ - public void enableLogging(Logger logger) { - super.enableLogging(logger); - setupLogger(requestHandler); - } - + private boolean sessionEnded = false; + /** * Set the configuration data for the handler. * @@ -127,102 +68,62 @@ } } + public void forceConnectionClose(final String message) { + getLogger().debug("forceConnectionClose: "+message); + ImapResponse response = new ImapResponse(outs); + response.byeResponse(message); + endSession(); + } + /** - * Set the Watchdog for use by this handler. - * - * @param theWatchdog the watchdog + * @see org.apache.james.smtpserver.SMTPSession#endSession() */ - public void setWatchdog( Watchdog theWatchdog ) - { - this.theWatchdog = theWatchdog; + public void endSession() { + sessionEnded = true; } /** - * Gets the Watchdog Target that should be used by Watchdogs managing - * this connection. - * - * @return the WatchdogTarget + * @see org.apache.james.smtpserver.SMTPSession#isSessionEnded() */ - WatchdogTarget getWatchdogTarget() - { - return theWatchdogTarget; - } - - public void forceConnectionClose(final String message) { - getLogger().debug("forceConnectionClose: "+message); - ImapResponse response = new ImapResponse(outs); - response.byeResponse(message); - resetHandler(); + public boolean isSessionEnded() { + return sessionEnded; } /** - * @see ConnectionHandler#handleConnection(Socket) + * Resets the handler data to a basic state. */ - public void handleConnection( Socket connection ) - throws IOException - { - handlerIsUp=true; - getLogger().debug("Accepting connection for "+connection.toString()); - // DEBUG - - String tcplogprefix= null; - if (theConfigData.doStreamdump()) { - String streamdumpDir=theConfigData.getStreamdumpDir(); - tcplogprefix= streamdumpDir+"/TCP-IMAP."+System.currentTimeMillis()+"."; - File logdir = new File(streamdumpDir); - if (!logdir.exists()) { - logdir.mkdir(); - } + public void resetHandler() { + if (handlerIsUp == false) { + return; } - String remoteHost = ""; - String remoteIP = ""; - + handlerIsUp = false; + + // Clear user data try { - this.socket = connection; - synchronized ( this ) { - handlerThread = Thread.currentThread(); - } - ins = socket.getInputStream(); - if (theConfigData.doStreamdump()) { - ins = new CopyInputStream(ins, new FileOutputStream( - tcplogprefix + "in")); - } - in = new BufferedReader( new InputStreamReader( socket.getInputStream(), "ASCII" ), 512 ); - remoteIP = socket.getInetAddress().getHostAddress(); - remoteHost = socket.getInetAddress().getHostName(); - } - catch ( IOException e ) { - if ( getLogger().isErrorEnabled() ) { - StringBuffer exceptionBuffer = - new StringBuffer( 256 ) - .append( "Cannot open connection from " ) - .append( remoteHost ) - .append( " (" ) - .append( remoteIP ) - .append( "): " ) - .append( e.getMessage() ); - getLogger().error( exceptionBuffer.toString(), e ); - } - throw e; + if (session != null) session.closeMailbox(); + } catch (Exception e) { + getLogger().error("session.cleanUp", e); } + session = null; - if ( getLogger().isInfoEnabled() ) { - StringBuffer logBuffer = - new StringBuffer( 128 ) - .append( "Connection from " ) - .append( remoteHost ) - .append( " (" ) - .append( remoteIP ) - .append( ") " ); - getLogger().info( logBuffer.toString() ); - } + // Clear config data + // Removed: we should never clean this one: + // theConfigData = null; + } + + protected void initHandler( Socket connection ) throws IOException { + handlerIsUp=true; + getLogger().debug("Accepting connection for "+connection.toString()); + // DEBUG + + super.initHandler(connection); + } + /** + * @see ConnectionHandler#handleConnection(Socket) + */ + protected void handleProtocol() throws IOException { try { - outs = new BufferedOutputStream( socket.getOutputStream(), 1024 ); - if (theConfigData.doStreamdump()) { - outs = new SplitOutputStream(outs, new FileOutputStream(tcplogprefix+"out")); - } - out = new InternetPrintWriter( outs, true ); ImapResponse response = new ImapResponse( outs ); // Write welcome message @@ -228,7 +129,7 @@ // Write welcome message StringBuffer responseBuffer = new StringBuffer( 256 ) - .append( VERSION ) + .append( softwaretype ) .append( " Server " ) .append( theConfigData.getHelloName() ) .append( " ready" ); @@ -234,6 +135,7 @@ .append( " ready" ); response.okResponse( null, responseBuffer.toString() ); + sessionEnded = false; session = new ImapSessionImpl( theConfigData.getMailboxManagerProvider(), theConfigData.getUsersRepository(), this, @@ -241,14 +143,9 @@ socket.getInetAddress().getHostAddress()); theWatchdog.start(); - while ( requestHandler.handleRequest( ins, outs, session ) ) { - if (!handlerIsUp) { - getLogger().debug("Handler has been resetted"); - return; - } + while ( handlerIsUp && !sessionEnded && requestHandler.handleRequest( in, outs, session ) ) { theWatchdog.reset(); } - // TODO is this unreachable code because of !handlerIsUp -> return? theWatchdog.stop(); @@ -272,12 +169,23 @@ } } - catch (Exception e) { - out.println("Error closing connection."); + catch (ProtocolException e) { + throw new RuntimeException(e.getMessage(),e); + } + } + + /** + * Method which will be colled on error + * + * @param e the RuntimeException + */ + protected void errorHandler(RuntimeException e) { + if (e != null && e.getCause() instanceof ProtocolException) { + out.println("Protocol exception."); out.flush(); StringBuffer exceptionBuffer = new StringBuffer( 128 ) - .append( "Exception on connection from " ) + .append( "Protocol exception during connection from " ) .append( remoteHost ) .append( " (" ) .append( remoteIP ) @@ -283,117 +191,10 @@ .append( remoteIP ) .append( ") : " ) .append( e.getMessage() ); - getLogger().error( exceptionBuffer.toString(), e ); - } - finally { - resetHandler(); - } - } - - /** - * Resets the handler data to a basic state. - */ - public void resetHandler() - { - if (handlerIsUp == false) { - return; - } - handlerIsUp = false; - if (theWatchdog != null) { - if (theWatchdog instanceof Disposable) { - ((Disposable) theWatchdog).dispose(); - } - theWatchdog = null; - } - - // Close and clear streams, sockets - - try { - if ( socket != null ) { - socket.close(); - socket = null; - } - } - catch ( IOException ioe ) { - // Ignoring exception on close - } - finally { - socket = null; - } - - try { - if ( in != null ) { - in.close(); - } - } - catch ( Exception e ) { - // Ignored - } - finally { - in = null; - } - - try { - if ( out != null ) { - out.close(); - } - } - catch ( Exception e ) { - // Ignored - } - finally { - out = null; - } - - try { - if ( outs != null ) { - outs.close(); - } - } - catch ( Exception e ) { - // Ignored - } - finally { - outs = null; - } - - synchronized ( this ) { - // Interrupt the thread to recover from internal hangs - if ( handlerThread != null ) { - handlerThread.interrupt(); - handlerThread = null; - } - } - - // Clear user data - - try { - if (session != null) session.closeMailbox(); - } catch (Exception e) { - getLogger().error("session.cleanUp", e); + getLogger().error( exceptionBuffer.toString(), e.getCause() ); + } else { + super.errorHandler(e); } - session = null; - - // Clear config data - theConfigData = null; - } - - /** - * A private inner class which serves as an adaptor - * between the WatchdogTarget interface and this - * handler class. - */ - private class IMAPWatchdogTarget - implements WatchdogTarget - { - - /** - * @see WatchdogTarget#execute() - */ - public void execute() - { - forceConnectionClose("IMAP Connection has idled out."); - } } } Index: java/org/apache/james/imapserver/ImapHandlerConfigurationData.java =================================================================== --- java/org/apache/james/imapserver/ImapHandlerConfigurationData.java (revision 468922) +++ java/org/apache/james/imapserver/ImapHandlerConfigurationData.java (working copy) @@ -52,10 +52,5 @@ UsersRepository getUsersRepository(); MailboxManagerProvider getMailboxManagerProvider(); - - boolean doStreamdump(); - - String getStreamdumpDir(); - } Index: java/org/apache/james/imapserver/ImapServer.java =================================================================== --- java/org/apache/james/imapserver/ImapServer.java (revision 475252) +++ java/org/apache/james/imapserver/ImapServer.java (working copy) @@ -19,10 +19,6 @@ package org.apache.james.imapserver; -import org.apache.avalon.cornerstone.services.connection.ConnectionHandler; -import org.apache.avalon.excalibur.pool.ObjectFactory; -import org.apache.avalon.excalibur.pool.Poolable; -import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.logger.Logger; @@ -31,8 +27,6 @@ import org.apache.james.core.AbstractJamesService; import org.apache.james.mailboxmanager.manager.MailboxManagerProvider; import org.apache.james.services.UsersRepository; -import org.apache.james.util.watchdog.Watchdog; -import org.apache.james.util.watchdog.WatchdogFactory; /** * TODO: this is a quick cut-and-paste hack from POP3Server. Should probably be @@ -60,11 +54,6 @@ private int lengthReset = 20 * 1024; /** - * The factory used to generate Watchdog objects - */ - private WatchdogFactory theWatchdogFactory; - - /** * The configuration data to be passed to the handler */ private IMAPHandlerConfigurationDataImpl theConfigData @@ -101,10 +90,6 @@ Configuration handlerConfiguration = configuration.getChild( "handler" ); lengthReset = handlerConfiguration.getChild( "lengthReset" ).getValueAsInteger( lengthReset ); getLogger().info( "The idle timeout will be reset every " + lengthReset + " bytes." ); - boolean streamdump=handlerConfiguration.getChild("streamdump").getAttributeAsBoolean("enabled", false); - theConfigData.setStreamDump(streamdump); - String streamdumpDir=handlerConfiguration.getChild("streamdump").getAttribute("directory", null); - theConfigData.setStreamDumpDir(streamdumpDir); } } @@ -109,20 +94,6 @@ } /** - * @see Initializable#initialize() - */ - public void initialize() throws Exception - { - - super.initialize(); - if ( !isEnabled() ) { - return; - } - - theWatchdogFactory = getWatchdogFactory(); - } - - /** * @see AbstractJamesService#getDefaultPort() */ protected int getDefaultPort() @@ -139,33 +110,6 @@ } /** - * @see org.apache.avalon.cornerstone.services.connection.AbstractHandlerFactory#newHandler() - */ - protected ConnectionHandler newHandler() - throws Exception - { - ImapHandler theHandler = (ImapHandler) theHandlerPool.get(); - - Watchdog theWatchdog = theWatchdogFactory.getWatchdog( theHandler.getWatchdogTarget() ); - - theHandler.setConfigurationData( theConfigData ); - - theHandler.setWatchdog( theWatchdog ); - return theHandler; - } - - /** - * @see org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory#releaseConnectionHandler(ConnectionHandler) - */ - public void releaseConnectionHandler( ConnectionHandler connectionHandler ) - { - if ( !( connectionHandler instanceof ImapHandler ) ) { - throw new IllegalArgumentException( "Attempted to return non-ImapHandler to pool." ); - } - theHandlerPool.put( ( Poolable ) connectionHandler ); - } - - /** * The factory for producing handlers. */ /** @@ -188,14 +132,6 @@ return ImapHandler.class; } - /** - * @see ObjectFactory#decommission(Object) - */ - public void decommission( Object object ) throws Exception - { - return; - } - /** * A class to provide POP3 handler configuration to the handlers */ @@ -203,8 +139,6 @@ implements ImapHandlerConfigurationData { - private String streamdumpDir = null; - private boolean streamdump = false; /** * @see ImapHandlerConfigurationData#getHelloName() */ @@ -213,15 +147,6 @@ return ImapServer.this.helloName; } - public void setStreamDumpDir(String streamdumpDir) { - this.streamdumpDir=streamdumpDir; - } - - public void setStreamDump(boolean streamdump) { - this.streamdump=streamdump; - - } - /** * @see ImapHandlerConfigurationData#getResetLength() */ @@ -242,14 +167,6 @@ return ImapServer.this.mailboxManagerProvider; } - public boolean doStreamdump() { - return streamdump; - } - - public String getStreamdumpDir() { - return streamdumpDir; - } - } protected Object getConfigurationData() { Index: test/org/apache/james/imapserver/mock/MockImapHandlerConfigurationData.java =================================================================== --- test/org/apache/james/imapserver/mock/MockImapHandlerConfigurationData.java (revision 468922) +++ test/org/apache/james/imapserver/mock/MockImapHandlerConfigurationData.java (working copy) @@ -2,7 +2,6 @@ import org.apache.james.imapserver.ImapHandlerConfigurationData; import org.apache.james.mailboxmanager.manager.MailboxManagerProvider; -import org.apache.james.mailboxmanager.torque.TorqueMailboxManagerProvider; import org.apache.james.services.MailServer; import org.apache.james.services.UsersRepository; @@ -50,12 +49,4 @@ return mailboxManagerProvider; } - public boolean doStreamdump() { - return true; - } - - public String getStreamdumpDir() { - return "streamdump"; - } - } Index: test/org/apache/james/imapserver/util/ImapServerLauncher.java =================================================================== --- test/org/apache/james/imapserver/util/ImapServerLauncher.java (revision 471771) +++ test/org/apache/james/imapserver/util/ImapServerLauncher.java (working copy) @@ -49,6 +49,7 @@ ImapHandler imapHandler=new ImapHandler(); imapHandler.enableLogging(new MockLogger()); imapHandler.setConfigurationData(theConfigData); + imapHandler.setStreamDumpDir("streamdump"); imapHandler.setWatchdog(new MockWatchdog()); System.out.println("Handle connection "+s); imapHandler.handleConnection(s);