You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by dl...@apache.org on 2010/05/10 10:58:54 UTC

svn commit: r942689 - in /mina/ftpserver/branches/1.0.x/core/src: main/java/org/apache/ftpserver/impl/FtpIoSession.java test/java/org/apache/ftpserver/clienttests/LoginTest.java

Author: dlat
Date: Mon May 10 08:58:54 2010
New Revision: 942689

URL: http://svn.apache.org/viewvc?rev=942689&view=rev
Log:
FTPSERVER-369 maxLogin is reached immediately. REIN command is not decreasing the login count.

Modified:
    mina/ftpserver/branches/1.0.x/core/src/main/java/org/apache/ftpserver/impl/FtpIoSession.java
    mina/ftpserver/branches/1.0.x/core/src/test/java/org/apache/ftpserver/clienttests/LoginTest.java

Modified: mina/ftpserver/branches/1.0.x/core/src/main/java/org/apache/ftpserver/impl/FtpIoSession.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/branches/1.0.x/core/src/main/java/org/apache/ftpserver/impl/FtpIoSession.java?rev=942689&r1=942688&r2=942689&view=diff
==============================================================================
--- mina/ftpserver/branches/1.0.x/core/src/main/java/org/apache/ftpserver/impl/FtpIoSession.java (original)
+++ mina/ftpserver/branches/1.0.x/core/src/main/java/org/apache/ftpserver/impl/FtpIoSession.java Mon May 10 08:58:54 2010
@@ -51,803 +51,829 @@ import org.apache.mina.core.session.IoSe
 import org.apache.mina.core.write.WriteRequest;
 import org.apache.mina.core.write.WriteRequestQueue;
 import org.apache.mina.filter.ssl.SslFilter;
+import org.slf4j.LoggerFactory;
 
 /**
  * <strong>Internal class, do not use directly.</strong>
- *
+ * 
  * @author <a href="http://mina.apache.org">Apache MINA Project</a> *
  */
 public class FtpIoSession implements IoSession {
 
-    /**
-     * Contains user name between USER and PASS commands
-     */
-    public static final String ATTRIBUTE_PREFIX = "org.apache.ftpserver.";
-
-    private static final String ATTRIBUTE_USER_ARGUMENT = ATTRIBUTE_PREFIX
-            + "user-argument";
-
-    private static final String ATTRIBUTE_SESSION_ID = ATTRIBUTE_PREFIX
-        + "session-id";
-    
-    private static final String ATTRIBUTE_USER = ATTRIBUTE_PREFIX + "user";
-
-    private static final String ATTRIBUTE_LANGUAGE = ATTRIBUTE_PREFIX
-            + "language";
-
-    private static final String ATTRIBUTE_LOGIN_TIME = ATTRIBUTE_PREFIX
-            + "login-time";
-
-    private static final String ATTRIBUTE_DATA_CONNECTION = ATTRIBUTE_PREFIX
-            + "data-connection";
-
-    private static final String ATTRIBUTE_FILE_SYSTEM = ATTRIBUTE_PREFIX
-            + "file-system";
-
-    private static final String ATTRIBUTE_RENAME_FROM = ATTRIBUTE_PREFIX
-            + "rename-from";
-
-    private static final String ATTRIBUTE_FILE_OFFSET = ATTRIBUTE_PREFIX
-            + "file-offset";
-
-    private static final String ATTRIBUTE_DATA_TYPE = ATTRIBUTE_PREFIX
-            + "data-type";
-
-    private static final String ATTRIBUTE_STRUCTURE = ATTRIBUTE_PREFIX
-            + "structure";
-
-    private static final String ATTRIBUTE_FAILED_LOGINS = ATTRIBUTE_PREFIX
-            + "failed-logins";
-
-    private static final String ATTRIBUTE_LISTENER = ATTRIBUTE_PREFIX
-            + "listener";
-
-    private static final String ATTRIBUTE_MAX_IDLE_TIME = ATTRIBUTE_PREFIX
-            + "max-idle-time";
-
-    private static final String ATTRIBUTE_LAST_ACCESS_TIME = ATTRIBUTE_PREFIX
-            + "last-access-time";
-
-    private static final String ATTRIBUTE_CACHED_REMOTE_ADDRESS = ATTRIBUTE_PREFIX
-            + "cached-remote-address";
-
-    private IoSession wrappedSession;
-
-    private FtpServerContext context;
-
-    /**
-     * Last reply that was sent to the client, if any. 
-     */
-    private FtpReply lastReply = null;
-
-    /* Begin wrapped IoSession methods */
-
-    /**
-     * @see IoSession#close()
-     */
-    public CloseFuture close() {
-        return wrappedSession.close();
-    }
-
-    /**
-     * @see IoSession#close(boolean)
-     */
-    public CloseFuture close(boolean immediately) {
-        return wrappedSession.close(immediately);
-    }
-
-    /**
-     * @see IoSession#containsAttribute(Object)
-     */
-    public boolean containsAttribute(Object key) {
-        return wrappedSession.containsAttribute(key);
-    }
-
-    /**
-     * @see IoSession#getAttachment()
-     */
-    @SuppressWarnings("deprecation")
-    public Object getAttachment() {
-        return wrappedSession.getAttachment();
-    }
-
-    /**
-     * @see IoSession#getAttribute(Object)
-     */
-    public Object getAttribute(Object key) {
-        return wrappedSession.getAttribute(key);
-    }
-
-    /**
-     * @see IoSession#getAttribute(Object, Object)
-     */
-    public Object getAttribute(Object key, Object defaultValue) {
-        return wrappedSession.getAttribute(key, defaultValue);
-    }
-
-    /**
-     * @see IoSession#getAttributeKeys()
-     */
-    public Set<Object> getAttributeKeys() {
-        return wrappedSession.getAttributeKeys();
-    }
-
-    /**
-     * @see IoSession#getBothIdleCount()
-     */
-    public int getBothIdleCount() {
-        return wrappedSession.getBothIdleCount();
-    }
-
-    /**
-     * @see IoSession#getCloseFuture()
-     */
-    public CloseFuture getCloseFuture() {
-        return wrappedSession.getCloseFuture();
-    }
-
-    /**
-     * @see IoSession#getConfig()
-     */
-    public IoSessionConfig getConfig() {
-        return wrappedSession.getConfig();
-    }
-
-    /**
-     * @see IoSession#getCreationTime()
-     */
-    public long getCreationTime() {
-        return wrappedSession.getCreationTime();
-    }
-
-    /**
-     * @see IoSession#getFilterChain()
-     */
-    public IoFilterChain getFilterChain() {
-        return wrappedSession.getFilterChain();
-    }
-
-    /**
-     * @see IoSession#getHandler()
-     */
-    public IoHandler getHandler() {
-        return wrappedSession.getHandler();
-    }
-
-    /**
-     * @see IoSession#getId()
-     */
-    public long getId() {
-        return wrappedSession.getId();
-    }
-
-    /**
-     * @see IoSession#getIdleCount(IdleStatus)
-     */
-    public int getIdleCount(IdleStatus status) {
-        return wrappedSession.getIdleCount(status);
-    }
-
-    /**
-     * @see IoSession#getLastBothIdleTime()
-     */
-    public long getLastBothIdleTime() {
-        return wrappedSession.getLastBothIdleTime();
-    }
-
-    /**
-     * @see IoSession#getLastIdleTime(IdleStatus)
-     */
-    public long getLastIdleTime(IdleStatus status) {
-        return wrappedSession.getLastIdleTime(status);
-    }
-
-    /**
-     * @see IoSession#getLastIoTime()
-     */
-    public long getLastIoTime() {
-        return wrappedSession.getLastIoTime();
-    }
-
-    /**
-     * @see IoSession#getLastReadTime()
-     */
-    public long getLastReadTime() {
-        return wrappedSession.getLastReadTime();
-    }
-
-    /**
-     * @see IoSession#getLastReaderIdleTime()
-     */
-    public long getLastReaderIdleTime() {
-        return wrappedSession.getLastReaderIdleTime();
-    }
-
-    /**
-     * @see IoSession#getLastWriteTime()
-     */
-    public long getLastWriteTime() {
-        return wrappedSession.getLastWriteTime();
-    }
-
-    /**
-     * @see IoSession#getLastWriterIdleTime()
-     */
-    public long getLastWriterIdleTime() {
-        return wrappedSession.getLastWriterIdleTime();
-    }
-
-    /**
-     * @see IoSession#getLocalAddress()
-     */
-    public SocketAddress getLocalAddress() {
-        return wrappedSession.getLocalAddress();
-    }
-
-    /**
-     * @see IoSession#getReadBytes()
-     */
-    public long getReadBytes() {
-        return wrappedSession.getReadBytes();
-    }
-
-    /**
-     * @see IoSession#getReadBytesThroughput()
-     */
-    public double getReadBytesThroughput() {
-        return wrappedSession.getReadBytesThroughput();
-    }
-
-    /**
-     * @see IoSession#getReadMessages()
-     */
-    public long getReadMessages() {
-        return wrappedSession.getReadMessages();
-    }
-
-    /**
-     * @see IoSession#getReadMessagesThroughput()
-     */
-    public double getReadMessagesThroughput() {
-        return wrappedSession.getReadMessagesThroughput();
-    }
-
-    /**
-     * @see IoSession#getReaderIdleCount()
-     */
-    public int getReaderIdleCount() {
-        return wrappedSession.getReaderIdleCount();
-    }
-
-    /**
-     * @see IoSession#getRemoteAddress()
-     */
-    public SocketAddress getRemoteAddress() {
-        // when closing a socket, the remote address might be reset to null
-        // therefore, we attempt to keep a cached copy around
-
-        SocketAddress address = wrappedSession.getRemoteAddress();
-        if (address == null
-                && containsAttribute(ATTRIBUTE_CACHED_REMOTE_ADDRESS)) {
-            return (SocketAddress) getAttribute(ATTRIBUTE_CACHED_REMOTE_ADDRESS);
-        } else {
-            setAttribute(ATTRIBUTE_CACHED_REMOTE_ADDRESS, address);
-            return address;
-        }
-    }
-
-    /**
-     * @see IoSession#getScheduledWriteBytes()
-     */
-    public long getScheduledWriteBytes() {
-        return wrappedSession.getScheduledWriteBytes();
-    }
-
-    /**
-     * @see IoSession#getScheduledWriteMessages()
-     */
-    public int getScheduledWriteMessages() {
-        return wrappedSession.getScheduledWriteMessages();
-    }
-
-    /**
-     * @see IoSession#getService()
-     */
-    public IoService getService() {
-        return wrappedSession.getService();
-    }
-
-    /**
-     * @see IoSession#getServiceAddress()
-     */
-    public SocketAddress getServiceAddress() {
-        return wrappedSession.getServiceAddress();
-    }
-
-    /**
-     * @see IoSession#getTransportMetadata()
-     */
-    public TransportMetadata getTransportMetadata() {
-        return wrappedSession.getTransportMetadata();
-    }
-
-    /**
-     * @see IoSession#getWriterIdleCount()
-     */
-    public int getWriterIdleCount() {
-        return wrappedSession.getWriterIdleCount();
-    }
-
-    /**
-     * @see IoSession#getWrittenBytes()
-     */
-    public long getWrittenBytes() {
-        return wrappedSession.getWrittenBytes();
-    }
-
-    /**
-     * @see IoSession#getWrittenBytesThroughput()
-     */
-    public double getWrittenBytesThroughput() {
-        return wrappedSession.getWrittenBytesThroughput();
-    }
-
-    /**
-     * @see IoSession#getWrittenMessages()
-     */
-    public long getWrittenMessages() {
-        return wrappedSession.getWrittenMessages();
-    }
-
-    /**
-     * @see IoSession#getWrittenMessagesThroughput()
-     */
-    public double getWrittenMessagesThroughput() {
-        return wrappedSession.getWrittenMessagesThroughput();
-    }
-
-    /**
-     * @see IoSession#isClosing()
-     */
-    public boolean isClosing() {
-        return wrappedSession.isClosing();
-    }
-
-    /**
-     * @see IoSession#isConnected()
-     */
-    public boolean isConnected() {
-        return wrappedSession.isConnected();
-    }
-
-    /**
-     * @see IoSession#isIdle(IdleStatus)
-     */
-    public boolean isIdle(IdleStatus status) {
-        return wrappedSession.isIdle(status);
-    }
-
-    /**
-     * @see IoSession#read()
-     */
-    public ReadFuture read() {
-        return wrappedSession.read();
-    }
-
-    /**
-     * @see IoSession#removeAttribute(Object)
-     */
-    public Object removeAttribute(Object key) {
-        return wrappedSession.removeAttribute(key);
-    }
-
-    /**
-     * @see IoSession#removeAttribute(Object, Object)
-     */
-    public boolean removeAttribute(Object key, Object value) {
-        return wrappedSession.removeAttribute(key, value);
-    }
-
-    /**
-     * @see IoSession#replaceAttribute(Object, Object, Object)
-     */
-    public boolean replaceAttribute(Object key, Object oldValue, Object newValue) {
-        return wrappedSession.replaceAttribute(key, oldValue, newValue);
-    }
-
-    /**
-     * @see IoSession#resumeRead()
-     */
-    public void resumeRead() {
-        wrappedSession.resumeRead();
-    }
-
-    /**
-     * @see IoSession#resumeWrite()
-     */
-    public void resumeWrite() {
-        wrappedSession.resumeWrite();
-    }
-
-    /**
-     * @see IoSession#setAttachment(Object)
-     */
-    @SuppressWarnings("deprecation")
-    public Object setAttachment(Object attachment) {
-        return wrappedSession.setAttachment(attachment);
-    }
-
-    /**
-     * @see IoSession#setAttribute(Object)
-     */
-    public Object setAttribute(Object key) {
-        return wrappedSession.setAttribute(key);
-    }
-
-    /**
-     * @see IoSession#setAttribute(Object, Object)
-     */
-    public Object setAttribute(Object key, Object value) {
-        return wrappedSession.setAttribute(key, value);
-    }
-
-    /**
-     * @see IoSession#setAttributeIfAbsent(Object)
-     */
-    public Object setAttributeIfAbsent(Object key) {
-        return wrappedSession.setAttributeIfAbsent(key);
-    }
-
-    /**
-     * @see IoSession#setAttributeIfAbsent(Object, Object)
-     */
-    public Object setAttributeIfAbsent(Object key, Object value) {
-        return wrappedSession.setAttributeIfAbsent(key, value);
-    }
-
-    /**
-     * @see IoSession#suspendRead()
-     */
-    public void suspendRead() {
-        wrappedSession.suspendRead();
-    }
-
-    /**
-     * @see IoSession#suspendWrite()
-     */
-    public void suspendWrite() {
-        wrappedSession.suspendWrite();
-    }
-
-    /**
-     * @see IoSession#write(Object)
-     */
-    public WriteFuture write(Object message) {
-        WriteFuture future = wrappedSession.write(message);
-        this.lastReply = (FtpReply) message;
-        return future;
-    }
-
-    /**
-     * @see IoSession#write(Object, SocketAddress)
-     */
-    public WriteFuture write(Object message, SocketAddress destination) {
-        WriteFuture future = wrappedSession.write(message, destination);
-        this.lastReply = (FtpReply) message;
-        return future;
-    }
-
-    /* End wrapped IoSession methods */
-
-    public void resetState() {
-        removeAttribute(ATTRIBUTE_RENAME_FROM);
-        removeAttribute(ATTRIBUTE_FILE_OFFSET);
-    }
-
-    public synchronized ServerDataConnectionFactory getDataConnection() {
-        if (containsAttribute(ATTRIBUTE_DATA_CONNECTION)) {
-            return (ServerDataConnectionFactory) getAttribute(ATTRIBUTE_DATA_CONNECTION);
-        } else {
-            IODataConnectionFactory dataCon = new IODataConnectionFactory(
-                    context, this);
-            dataCon
-                    .setServerControlAddress(((InetSocketAddress) getLocalAddress())
-                            .getAddress());
-            setAttribute(ATTRIBUTE_DATA_CONNECTION, dataCon);
-
-            return dataCon;
-        }
-    }
-
-    public FileSystemView getFileSystemView() {
-        return (FileSystemView) getAttribute(ATTRIBUTE_FILE_SYSTEM);
-    }
-
-    public User getUser() {
-        return (User) getAttribute(ATTRIBUTE_USER);
-    }
-
-    /**
-     * Is logged-in
-     */
-    public boolean isLoggedIn() {
-        return containsAttribute(ATTRIBUTE_USER);
-    }
-
-    public Listener getListener() {
-        return (Listener) getAttribute(ATTRIBUTE_LISTENER);
-    }
-
-    public void setListener(Listener listener) {
-        setAttribute(ATTRIBUTE_LISTENER, listener);
-    }
-
-    public FtpSession getFtpletSession() {
-        return new DefaultFtpSession(this);
-    }
-
-    public String getLanguage() {
-        return (String) getAttribute(ATTRIBUTE_LANGUAGE);
-    }
-
-    public void setLanguage(String language) {
-        setAttribute(ATTRIBUTE_LANGUAGE, language);
-
-    }
-
-    public String getUserArgument() {
-        return (String) getAttribute(ATTRIBUTE_USER_ARGUMENT);
-    }
-
-    public void setUser(User user) {
-        setAttribute(ATTRIBUTE_USER, user);
-
-    }
-
-    public void setUserArgument(String userArgument) {
-        setAttribute(ATTRIBUTE_USER_ARGUMENT, userArgument);
-
-    }
-
-    public int getMaxIdleTime() {
-        return (Integer) getAttribute(ATTRIBUTE_MAX_IDLE_TIME, 0);
-    }
-
-    public void setMaxIdleTime(int maxIdleTime) {
-        setAttribute(ATTRIBUTE_MAX_IDLE_TIME, maxIdleTime);
-        
-        int listenerTimeout = getListener().getIdleTimeout();
-        
-        // the listener timeout should be the upper limit, unless set to unlimited
-        // if the user limit is set to be unlimited, use the listener value is the threshold 
-        //     (already used as the default for all sessions)
-        // else, if the user limit is less than the listener idle time, use the user limit 
-        if(listenerTimeout <= 0 || 
-                (maxIdleTime > 0 && maxIdleTime < listenerTimeout)) {
-            wrappedSession.getConfig().setBothIdleTime(maxIdleTime);
-        }
-    }
-
-    public synchronized void increaseFailedLogins() {
-        int failedLogins = (Integer) getAttribute(ATTRIBUTE_FAILED_LOGINS, 0);
-        failedLogins++;
-        setAttribute(ATTRIBUTE_FAILED_LOGINS, failedLogins);
-    }
-
-    public int getFailedLogins() {
-        return (Integer) getAttribute(ATTRIBUTE_FAILED_LOGINS, 0);
-    }
-
-    public void setLogin(FileSystemView fsview) {
-        setAttribute(ATTRIBUTE_LOGIN_TIME, new Date());
-        setAttribute(ATTRIBUTE_FILE_SYSTEM, fsview);
-    }
-
-    public void reinitialize() {
-        removeAttribute(ATTRIBUTE_USER);
-        removeAttribute(ATTRIBUTE_USER_ARGUMENT);
-        removeAttribute(ATTRIBUTE_LOGIN_TIME);
-        removeAttribute(ATTRIBUTE_FILE_SYSTEM);
-        removeAttribute(ATTRIBUTE_RENAME_FROM);
-        removeAttribute(ATTRIBUTE_FILE_OFFSET);
-    }
-
-    public void setFileOffset(long fileOffset) {
-        setAttribute(ATTRIBUTE_FILE_OFFSET, fileOffset);
-
-    }
-
-    public void setRenameFrom(FtpFile renFr) {
-        setAttribute(ATTRIBUTE_RENAME_FROM, renFr);
-
-    }
-
-    public FtpFile getRenameFrom() {
-        return (FtpFile) getAttribute(ATTRIBUTE_RENAME_FROM);
-    }
-
-    public long getFileOffset() {
-        return (Long) getAttribute(ATTRIBUTE_FILE_OFFSET, 0L);
-    }
-
-    public void setStructure(Structure structure) {
-        setAttribute(ATTRIBUTE_STRUCTURE, structure);
-    }
-
-    public void setDataType(DataType dataType) {
-        setAttribute(ATTRIBUTE_DATA_TYPE, dataType);
-
-    }
-
-    /**
-     * @see FtpSession#getSessionId()
-     */
-    public UUID getSessionId() {
-        synchronized (wrappedSession) {
-            if(!wrappedSession.containsAttribute(ATTRIBUTE_SESSION_ID)) {
-                wrappedSession.setAttribute(ATTRIBUTE_SESSION_ID, UUID.randomUUID());
-            }
-            return (UUID) wrappedSession.getAttribute(ATTRIBUTE_SESSION_ID);
-        }
-    }
-    
-    public FtpIoSession(IoSession wrappedSession, FtpServerContext context) {
-        this.wrappedSession = wrappedSession;
-        this.context = context;
-    }
-
-    public Structure getStructure() {
-        return (Structure) getAttribute(ATTRIBUTE_STRUCTURE, Structure.FILE);
-    }
-
-    public DataType getDataType() {
-        return (DataType) getAttribute(ATTRIBUTE_DATA_TYPE, DataType.ASCII);
-    }
-
-    public Date getLoginTime() {
-        return (Date) getAttribute(ATTRIBUTE_LOGIN_TIME);
-    }
-
-    public Date getLastAccessTime() {
-        return (Date) getAttribute(ATTRIBUTE_LAST_ACCESS_TIME);
-    }
-
-    public Certificate[] getClientCertificates() {
-        if (getFilterChain().contains(SslFilter.class)) {
-            SslFilter sslFilter = (SslFilter) getFilterChain().get(
-                    SslFilter.class);
-
-            SSLSession sslSession = sslFilter.getSslSession(this);
-
-            if (sslSession != null) {
-                try {
-                    return sslSession.getPeerCertificates();
-                } catch (SSLPeerUnverifiedException e) {
-                    // ignore, certificate will not be available to the session
-                }
-            }
-
-        }
-
-        // no certificates available
-        return null;
-
-    }
-
-    public void updateLastAccessTime() {
-        setAttribute(ATTRIBUTE_LAST_ACCESS_TIME, new Date());
-
-    }
-
-    /**
-     * @see IoSession#getCurrentWriteMessage()
-     */
-    public Object getCurrentWriteMessage() {
-        return wrappedSession.getCurrentWriteMessage();
-    }
-
-    /**
-     * @see IoSession#getCurrentWriteRequest()
-     */
-    public WriteRequest getCurrentWriteRequest() {
-        return wrappedSession.getCurrentWriteRequest();
-    }
-
-    /**
-     * @see IoSession#isBothIdle()
-     */
-    public boolean isBothIdle() {
-        return wrappedSession.isBothIdle();
-    }
-
-    /**
-     * @see IoSession#isReaderIdle()
-     */
-    public boolean isReaderIdle() {
-        return wrappedSession.isReaderIdle();
-    }
-
-    /**
-     * @see IoSession#isWriterIdle()
-     */
-    public boolean isWriterIdle() {
-        return wrappedSession.isWriterIdle();
-    }
-
-    /**
-     * Indicates whether the control socket for this session is secure, that is,
-     * running over SSL/TLS
-     * 
-     * @return true if the control socket is secured
-     */
-    public boolean isSecure() {
-        return getFilterChain().contains(SslFilter.class);
-    }
-
-    /**
-     * Increase the number of bytes written on the data connection
-     * @param increment The number of bytes written
-     */
-    public void increaseWrittenDataBytes(int increment) {
-        if (wrappedSession instanceof AbstractIoSession) {
-            ((AbstractIoSession) wrappedSession)
-                    .increaseScheduledWriteBytes(increment);
-            ((AbstractIoSession) wrappedSession).increaseWrittenBytes(
-                    increment, System.currentTimeMillis());
-        }
-    }
-
-    /**
-     * Increase the number of bytes read on the data connection
-     * @param increment The number of bytes written
-     */
-    public void increaseReadDataBytes(int increment) {
-        if (wrappedSession instanceof AbstractIoSession) {
-            ((AbstractIoSession) wrappedSession).increaseReadBytes(increment,
-                    System.currentTimeMillis());
-        }
-    }
-
-    /**
-     * Returns the last reply that was sent to the client. 
-     * @return the last reply that was sent to the client.
-     */
-    public FtpReply getLastReply() {
-        return lastReply;
-    }
-
-    /**
-     * @see IoSession#getWriteRequestQueue()
-     */
-    public WriteRequestQueue getWriteRequestQueue() {
-        return wrappedSession.getWriteRequestQueue();
-    }
-
-    /**
-     * @see IoSession#isReadSuspended()
-     */
-    public boolean isReadSuspended() {
-        return wrappedSession.isReadSuspended();
-    }
-
-    /**
-     * @see IoSession#isWriteSuspended()
-     */
-    public boolean isWriteSuspended() {
-        return wrappedSession.isWriteSuspended();
-    }
-
-    /**
-     * @see IoSession#setCurrentWriteRequest(WriteRequest)
-     */
-    public void setCurrentWriteRequest(WriteRequest currentWriteRequest) {
-        wrappedSession.setCurrentWriteRequest(currentWriteRequest);
-    }
-
-    /**
-     * @see IoSession#updateThroughput(long, boolean)
-     */
-    public void updateThroughput(long currentTime, boolean force) {
-        wrappedSession.updateThroughput(currentTime, force);
-    }
+	/**
+	 * Contains user name between USER and PASS commands
+	 */
+	public static final String ATTRIBUTE_PREFIX = "org.apache.ftpserver.";
+
+	private static final String ATTRIBUTE_USER_ARGUMENT = ATTRIBUTE_PREFIX
+			+ "user-argument";
+
+	private static final String ATTRIBUTE_SESSION_ID = ATTRIBUTE_PREFIX
+			+ "session-id";
+
+	private static final String ATTRIBUTE_USER = ATTRIBUTE_PREFIX + "user";
+
+	private static final String ATTRIBUTE_LANGUAGE = ATTRIBUTE_PREFIX
+			+ "language";
+
+	private static final String ATTRIBUTE_LOGIN_TIME = ATTRIBUTE_PREFIX
+			+ "login-time";
+
+	private static final String ATTRIBUTE_DATA_CONNECTION = ATTRIBUTE_PREFIX
+			+ "data-connection";
+
+	private static final String ATTRIBUTE_FILE_SYSTEM = ATTRIBUTE_PREFIX
+			+ "file-system";
+
+	private static final String ATTRIBUTE_RENAME_FROM = ATTRIBUTE_PREFIX
+			+ "rename-from";
+
+	private static final String ATTRIBUTE_FILE_OFFSET = ATTRIBUTE_PREFIX
+			+ "file-offset";
+
+	private static final String ATTRIBUTE_DATA_TYPE = ATTRIBUTE_PREFIX
+			+ "data-type";
+
+	private static final String ATTRIBUTE_STRUCTURE = ATTRIBUTE_PREFIX
+			+ "structure";
+
+	private static final String ATTRIBUTE_FAILED_LOGINS = ATTRIBUTE_PREFIX
+			+ "failed-logins";
+
+	private static final String ATTRIBUTE_LISTENER = ATTRIBUTE_PREFIX
+			+ "listener";
+
+	private static final String ATTRIBUTE_MAX_IDLE_TIME = ATTRIBUTE_PREFIX
+			+ "max-idle-time";
+
+	private static final String ATTRIBUTE_LAST_ACCESS_TIME = ATTRIBUTE_PREFIX
+			+ "last-access-time";
+
+	private static final String ATTRIBUTE_CACHED_REMOTE_ADDRESS = ATTRIBUTE_PREFIX
+			+ "cached-remote-address";
+
+	private IoSession wrappedSession;
+
+	private FtpServerContext context;
+
+	/**
+	 * Last reply that was sent to the client, if any.
+	 */
+	private FtpReply lastReply = null;
+
+	/* Begin wrapped IoSession methods */
+
+	/**
+	 * @see IoSession#close()
+	 */
+	public CloseFuture close() {
+		return wrappedSession.close();
+	}
+
+	/**
+	 * @see IoSession#close(boolean)
+	 */
+	public CloseFuture close(boolean immediately) {
+		return wrappedSession.close(immediately);
+	}
+
+	/**
+	 * @see IoSession#containsAttribute(Object)
+	 */
+	public boolean containsAttribute(Object key) {
+		return wrappedSession.containsAttribute(key);
+	}
+
+	/**
+	 * @see IoSession#getAttachment()
+	 */
+	@SuppressWarnings("deprecation")
+	public Object getAttachment() {
+		return wrappedSession.getAttachment();
+	}
+
+	/**
+	 * @see IoSession#getAttribute(Object)
+	 */
+	public Object getAttribute(Object key) {
+		return wrappedSession.getAttribute(key);
+	}
+
+	/**
+	 * @see IoSession#getAttribute(Object, Object)
+	 */
+	public Object getAttribute(Object key, Object defaultValue) {
+		return wrappedSession.getAttribute(key, defaultValue);
+	}
+
+	/**
+	 * @see IoSession#getAttributeKeys()
+	 */
+	public Set<Object> getAttributeKeys() {
+		return wrappedSession.getAttributeKeys();
+	}
+
+	/**
+	 * @see IoSession#getBothIdleCount()
+	 */
+	public int getBothIdleCount() {
+		return wrappedSession.getBothIdleCount();
+	}
+
+	/**
+	 * @see IoSession#getCloseFuture()
+	 */
+	public CloseFuture getCloseFuture() {
+		return wrappedSession.getCloseFuture();
+	}
+
+	/**
+	 * @see IoSession#getConfig()
+	 */
+	public IoSessionConfig getConfig() {
+		return wrappedSession.getConfig();
+	}
+
+	/**
+	 * @see IoSession#getCreationTime()
+	 */
+	public long getCreationTime() {
+		return wrappedSession.getCreationTime();
+	}
+
+	/**
+	 * @see IoSession#getFilterChain()
+	 */
+	public IoFilterChain getFilterChain() {
+		return wrappedSession.getFilterChain();
+	}
+
+	/**
+	 * @see IoSession#getHandler()
+	 */
+	public IoHandler getHandler() {
+		return wrappedSession.getHandler();
+	}
+
+	/**
+	 * @see IoSession#getId()
+	 */
+	public long getId() {
+		return wrappedSession.getId();
+	}
+
+	/**
+	 * @see IoSession#getIdleCount(IdleStatus)
+	 */
+	public int getIdleCount(IdleStatus status) {
+		return wrappedSession.getIdleCount(status);
+	}
+
+	/**
+	 * @see IoSession#getLastBothIdleTime()
+	 */
+	public long getLastBothIdleTime() {
+		return wrappedSession.getLastBothIdleTime();
+	}
+
+	/**
+	 * @see IoSession#getLastIdleTime(IdleStatus)
+	 */
+	public long getLastIdleTime(IdleStatus status) {
+		return wrappedSession.getLastIdleTime(status);
+	}
+
+	/**
+	 * @see IoSession#getLastIoTime()
+	 */
+	public long getLastIoTime() {
+		return wrappedSession.getLastIoTime();
+	}
+
+	/**
+	 * @see IoSession#getLastReadTime()
+	 */
+	public long getLastReadTime() {
+		return wrappedSession.getLastReadTime();
+	}
+
+	/**
+	 * @see IoSession#getLastReaderIdleTime()
+	 */
+	public long getLastReaderIdleTime() {
+		return wrappedSession.getLastReaderIdleTime();
+	}
+
+	/**
+	 * @see IoSession#getLastWriteTime()
+	 */
+	public long getLastWriteTime() {
+		return wrappedSession.getLastWriteTime();
+	}
+
+	/**
+	 * @see IoSession#getLastWriterIdleTime()
+	 */
+	public long getLastWriterIdleTime() {
+		return wrappedSession.getLastWriterIdleTime();
+	}
+
+	/**
+	 * @see IoSession#getLocalAddress()
+	 */
+	public SocketAddress getLocalAddress() {
+		return wrappedSession.getLocalAddress();
+	}
+
+	/**
+	 * @see IoSession#getReadBytes()
+	 */
+	public long getReadBytes() {
+		return wrappedSession.getReadBytes();
+	}
+
+	/**
+	 * @see IoSession#getReadBytesThroughput()
+	 */
+	public double getReadBytesThroughput() {
+		return wrappedSession.getReadBytesThroughput();
+	}
+
+	/**
+	 * @see IoSession#getReadMessages()
+	 */
+	public long getReadMessages() {
+		return wrappedSession.getReadMessages();
+	}
+
+	/**
+	 * @see IoSession#getReadMessagesThroughput()
+	 */
+	public double getReadMessagesThroughput() {
+		return wrappedSession.getReadMessagesThroughput();
+	}
+
+	/**
+	 * @see IoSession#getReaderIdleCount()
+	 */
+	public int getReaderIdleCount() {
+		return wrappedSession.getReaderIdleCount();
+	}
+
+	/**
+	 * @see IoSession#getRemoteAddress()
+	 */
+	public SocketAddress getRemoteAddress() {
+		// when closing a socket, the remote address might be reset to null
+		// therefore, we attempt to keep a cached copy around
+
+		SocketAddress address = wrappedSession.getRemoteAddress();
+		if (address == null
+				&& containsAttribute(ATTRIBUTE_CACHED_REMOTE_ADDRESS)) {
+			return (SocketAddress) getAttribute(ATTRIBUTE_CACHED_REMOTE_ADDRESS);
+		} else {
+			setAttribute(ATTRIBUTE_CACHED_REMOTE_ADDRESS, address);
+			return address;
+		}
+	}
+
+	/**
+	 * @see IoSession#getScheduledWriteBytes()
+	 */
+	public long getScheduledWriteBytes() {
+		return wrappedSession.getScheduledWriteBytes();
+	}
+
+	/**
+	 * @see IoSession#getScheduledWriteMessages()
+	 */
+	public int getScheduledWriteMessages() {
+		return wrappedSession.getScheduledWriteMessages();
+	}
+
+	/**
+	 * @see IoSession#getService()
+	 */
+	public IoService getService() {
+		return wrappedSession.getService();
+	}
+
+	/**
+	 * @see IoSession#getServiceAddress()
+	 */
+	public SocketAddress getServiceAddress() {
+		return wrappedSession.getServiceAddress();
+	}
+
+	/**
+	 * @see IoSession#getTransportMetadata()
+	 */
+	public TransportMetadata getTransportMetadata() {
+		return wrappedSession.getTransportMetadata();
+	}
+
+	/**
+	 * @see IoSession#getWriterIdleCount()
+	 */
+	public int getWriterIdleCount() {
+		return wrappedSession.getWriterIdleCount();
+	}
+
+	/**
+	 * @see IoSession#getWrittenBytes()
+	 */
+	public long getWrittenBytes() {
+		return wrappedSession.getWrittenBytes();
+	}
+
+	/**
+	 * @see IoSession#getWrittenBytesThroughput()
+	 */
+	public double getWrittenBytesThroughput() {
+		return wrappedSession.getWrittenBytesThroughput();
+	}
+
+	/**
+	 * @see IoSession#getWrittenMessages()
+	 */
+	public long getWrittenMessages() {
+		return wrappedSession.getWrittenMessages();
+	}
+
+	/**
+	 * @see IoSession#getWrittenMessagesThroughput()
+	 */
+	public double getWrittenMessagesThroughput() {
+		return wrappedSession.getWrittenMessagesThroughput();
+	}
+
+	/**
+	 * @see IoSession#isClosing()
+	 */
+	public boolean isClosing() {
+		return wrappedSession.isClosing();
+	}
+
+	/**
+	 * @see IoSession#isConnected()
+	 */
+	public boolean isConnected() {
+		return wrappedSession.isConnected();
+	}
+
+	/**
+	 * @see IoSession#isIdle(IdleStatus)
+	 */
+	public boolean isIdle(IdleStatus status) {
+		return wrappedSession.isIdle(status);
+	}
+
+	/**
+	 * @see IoSession#read()
+	 */
+	public ReadFuture read() {
+		return wrappedSession.read();
+	}
+
+	/**
+	 * @see IoSession#removeAttribute(Object)
+	 */
+	public Object removeAttribute(Object key) {
+		return wrappedSession.removeAttribute(key);
+	}
+
+	/**
+	 * @see IoSession#removeAttribute(Object, Object)
+	 */
+	public boolean removeAttribute(Object key, Object value) {
+		return wrappedSession.removeAttribute(key, value);
+	}
+
+	/**
+	 * @see IoSession#replaceAttribute(Object, Object, Object)
+	 */
+	public boolean replaceAttribute(Object key, Object oldValue, Object newValue) {
+		return wrappedSession.replaceAttribute(key, oldValue, newValue);
+	}
+
+	/**
+	 * @see IoSession#resumeRead()
+	 */
+	public void resumeRead() {
+		wrappedSession.resumeRead();
+	}
+
+	/**
+	 * @see IoSession#resumeWrite()
+	 */
+	public void resumeWrite() {
+		wrappedSession.resumeWrite();
+	}
+
+	/**
+	 * @see IoSession#setAttachment(Object)
+	 */
+	@SuppressWarnings("deprecation")
+	public Object setAttachment(Object attachment) {
+		return wrappedSession.setAttachment(attachment);
+	}
+
+	/**
+	 * @see IoSession#setAttribute(Object)
+	 */
+	public Object setAttribute(Object key) {
+		return wrappedSession.setAttribute(key);
+	}
+
+	/**
+	 * @see IoSession#setAttribute(Object, Object)
+	 */
+	public Object setAttribute(Object key, Object value) {
+		return wrappedSession.setAttribute(key, value);
+	}
+
+	/**
+	 * @see IoSession#setAttributeIfAbsent(Object)
+	 */
+	public Object setAttributeIfAbsent(Object key) {
+		return wrappedSession.setAttributeIfAbsent(key);
+	}
+
+	/**
+	 * @see IoSession#setAttributeIfAbsent(Object, Object)
+	 */
+	public Object setAttributeIfAbsent(Object key, Object value) {
+		return wrappedSession.setAttributeIfAbsent(key, value);
+	}
+
+	/**
+	 * @see IoSession#suspendRead()
+	 */
+	public void suspendRead() {
+		wrappedSession.suspendRead();
+	}
+
+	/**
+	 * @see IoSession#suspendWrite()
+	 */
+	public void suspendWrite() {
+		wrappedSession.suspendWrite();
+	}
+
+	/**
+	 * @see IoSession#write(Object)
+	 */
+	public WriteFuture write(Object message) {
+		WriteFuture future = wrappedSession.write(message);
+		this.lastReply = (FtpReply) message;
+		return future;
+	}
+
+	/**
+	 * @see IoSession#write(Object, SocketAddress)
+	 */
+	public WriteFuture write(Object message, SocketAddress destination) {
+		WriteFuture future = wrappedSession.write(message, destination);
+		this.lastReply = (FtpReply) message;
+		return future;
+	}
+
+	/* End wrapped IoSession methods */
+
+	public void resetState() {
+		removeAttribute(ATTRIBUTE_RENAME_FROM);
+		removeAttribute(ATTRIBUTE_FILE_OFFSET);
+	}
+
+	public synchronized ServerDataConnectionFactory getDataConnection() {
+		if (containsAttribute(ATTRIBUTE_DATA_CONNECTION)) {
+			return (ServerDataConnectionFactory) getAttribute(ATTRIBUTE_DATA_CONNECTION);
+		} else {
+			IODataConnectionFactory dataCon = new IODataConnectionFactory(
+					context, this);
+			dataCon
+					.setServerControlAddress(((InetSocketAddress) getLocalAddress())
+							.getAddress());
+			setAttribute(ATTRIBUTE_DATA_CONNECTION, dataCon);
+
+			return dataCon;
+		}
+	}
+
+	public FileSystemView getFileSystemView() {
+		return (FileSystemView) getAttribute(ATTRIBUTE_FILE_SYSTEM);
+	}
+
+	public User getUser() {
+		return (User) getAttribute(ATTRIBUTE_USER);
+	}
+
+	/**
+	 * Is logged-in
+	 */
+	public boolean isLoggedIn() {
+		return containsAttribute(ATTRIBUTE_USER);
+	}
+
+	public Listener getListener() {
+		return (Listener) getAttribute(ATTRIBUTE_LISTENER);
+	}
+
+	public void setListener(Listener listener) {
+		setAttribute(ATTRIBUTE_LISTENER, listener);
+	}
+
+	public FtpSession getFtpletSession() {
+		return new DefaultFtpSession(this);
+	}
+
+	public String getLanguage() {
+		return (String) getAttribute(ATTRIBUTE_LANGUAGE);
+	}
+
+	public void setLanguage(String language) {
+		setAttribute(ATTRIBUTE_LANGUAGE, language);
+
+	}
+
+	public String getUserArgument() {
+		return (String) getAttribute(ATTRIBUTE_USER_ARGUMENT);
+	}
+
+	public void setUser(User user) {
+		setAttribute(ATTRIBUTE_USER, user);
+
+	}
+
+	public void setUserArgument(String userArgument) {
+		setAttribute(ATTRIBUTE_USER_ARGUMENT, userArgument);
+
+	}
+
+	public int getMaxIdleTime() {
+		return (Integer) getAttribute(ATTRIBUTE_MAX_IDLE_TIME, 0);
+	}
+
+	public void setMaxIdleTime(int maxIdleTime) {
+		setAttribute(ATTRIBUTE_MAX_IDLE_TIME, maxIdleTime);
+
+		int listenerTimeout = getListener().getIdleTimeout();
+
+		// the listener timeout should be the upper limit, unless set to
+		// unlimited
+		// if the user limit is set to be unlimited, use the listener value is
+		// the threshold
+		// (already used as the default for all sessions)
+		// else, if the user limit is less than the listener idle time, use the
+		// user limit
+		if (listenerTimeout <= 0
+				|| (maxIdleTime > 0 && maxIdleTime < listenerTimeout)) {
+			wrappedSession.getConfig().setBothIdleTime(maxIdleTime);
+		}
+	}
+
+	public synchronized void increaseFailedLogins() {
+		int failedLogins = (Integer) getAttribute(ATTRIBUTE_FAILED_LOGINS, 0);
+		failedLogins++;
+		setAttribute(ATTRIBUTE_FAILED_LOGINS, failedLogins);
+	}
+
+	public int getFailedLogins() {
+		return (Integer) getAttribute(ATTRIBUTE_FAILED_LOGINS, 0);
+	}
+
+	public void setLogin(FileSystemView fsview) {
+		setAttribute(ATTRIBUTE_LOGIN_TIME, new Date());
+		setAttribute(ATTRIBUTE_FILE_SYSTEM, fsview);
+	}
+
+	public void reinitialize() {
+		logoutUser();
+		removeAttribute(ATTRIBUTE_USER);
+		removeAttribute(ATTRIBUTE_USER_ARGUMENT);
+		removeAttribute(ATTRIBUTE_LOGIN_TIME);
+		removeAttribute(ATTRIBUTE_FILE_SYSTEM);
+		removeAttribute(ATTRIBUTE_RENAME_FROM);
+		removeAttribute(ATTRIBUTE_FILE_OFFSET);
+	}
+
+	public void logoutUser() {
+		ServerFtpStatistics stats = ((ServerFtpStatistics) context
+				.getFtpStatistics());
+		if (stats != null) {
+			stats.setLogout(this);
+			LoggerFactory.getLogger(this.getClass()).debug(
+					"Statistics login decreased due to user logout");
+		} else {
+			LoggerFactory
+					.getLogger(this.getClass())
+					.warn(
+							"Statistics not available in session, can not decrease login  count");
+		}
+	}
+
+	public void setFileOffset(long fileOffset) {
+		setAttribute(ATTRIBUTE_FILE_OFFSET, fileOffset);
+
+	}
+
+	public void setRenameFrom(FtpFile renFr) {
+		setAttribute(ATTRIBUTE_RENAME_FROM, renFr);
+
+	}
+
+	public FtpFile getRenameFrom() {
+		return (FtpFile) getAttribute(ATTRIBUTE_RENAME_FROM);
+	}
+
+	public long getFileOffset() {
+		return (Long) getAttribute(ATTRIBUTE_FILE_OFFSET, 0L);
+	}
+
+	public void setStructure(Structure structure) {
+		setAttribute(ATTRIBUTE_STRUCTURE, structure);
+	}
+
+	public void setDataType(DataType dataType) {
+		setAttribute(ATTRIBUTE_DATA_TYPE, dataType);
+
+	}
+
+	/**
+	 * @see FtpSession#getSessionId()
+	 */
+	public UUID getSessionId() {
+		synchronized (wrappedSession) {
+			if (!wrappedSession.containsAttribute(ATTRIBUTE_SESSION_ID)) {
+				wrappedSession.setAttribute(ATTRIBUTE_SESSION_ID, UUID
+						.randomUUID());
+			}
+			return (UUID) wrappedSession.getAttribute(ATTRIBUTE_SESSION_ID);
+		}
+	}
+
+	public FtpIoSession(IoSession wrappedSession, FtpServerContext context) {
+		this.wrappedSession = wrappedSession;
+		this.context = context;
+	}
+
+	public Structure getStructure() {
+		return (Structure) getAttribute(ATTRIBUTE_STRUCTURE, Structure.FILE);
+	}
+
+	public DataType getDataType() {
+		return (DataType) getAttribute(ATTRIBUTE_DATA_TYPE, DataType.ASCII);
+	}
+
+	public Date getLoginTime() {
+		return (Date) getAttribute(ATTRIBUTE_LOGIN_TIME);
+	}
+
+	public Date getLastAccessTime() {
+		return (Date) getAttribute(ATTRIBUTE_LAST_ACCESS_TIME);
+	}
+
+	public Certificate[] getClientCertificates() {
+		if (getFilterChain().contains(SslFilter.class)) {
+			SslFilter sslFilter = (SslFilter) getFilterChain().get(
+					SslFilter.class);
+
+			SSLSession sslSession = sslFilter.getSslSession(this);
+
+			if (sslSession != null) {
+				try {
+					return sslSession.getPeerCertificates();
+				} catch (SSLPeerUnverifiedException e) {
+					// ignore, certificate will not be available to the session
+				}
+			}
+
+		}
+
+		// no certificates available
+		return null;
+
+	}
+
+	public void updateLastAccessTime() {
+		setAttribute(ATTRIBUTE_LAST_ACCESS_TIME, new Date());
+
+	}
+
+	/**
+	 * @see IoSession#getCurrentWriteMessage()
+	 */
+	public Object getCurrentWriteMessage() {
+		return wrappedSession.getCurrentWriteMessage();
+	}
+
+	/**
+	 * @see IoSession#getCurrentWriteRequest()
+	 */
+	public WriteRequest getCurrentWriteRequest() {
+		return wrappedSession.getCurrentWriteRequest();
+	}
+
+	/**
+	 * @see IoSession#isBothIdle()
+	 */
+	public boolean isBothIdle() {
+		return wrappedSession.isBothIdle();
+	}
+
+	/**
+	 * @see IoSession#isReaderIdle()
+	 */
+	public boolean isReaderIdle() {
+		return wrappedSession.isReaderIdle();
+	}
+
+	/**
+	 * @see IoSession#isWriterIdle()
+	 */
+	public boolean isWriterIdle() {
+		return wrappedSession.isWriterIdle();
+	}
+
+	/**
+	 * Indicates whether the control socket for this session is secure, that is,
+	 * running over SSL/TLS
+	 * 
+	 * @return true if the control socket is secured
+	 */
+	public boolean isSecure() {
+		return getFilterChain().contains(SslFilter.class);
+	}
+
+	/**
+	 * Increase the number of bytes written on the data connection
+	 * 
+	 * @param increment
+	 *            The number of bytes written
+	 */
+	public void increaseWrittenDataBytes(int increment) {
+		if (wrappedSession instanceof AbstractIoSession) {
+			((AbstractIoSession) wrappedSession)
+					.increaseScheduledWriteBytes(increment);
+			((AbstractIoSession) wrappedSession).increaseWrittenBytes(
+					increment, System.currentTimeMillis());
+		}
+	}
+
+	/**
+	 * Increase the number of bytes read on the data connection
+	 * 
+	 * @param increment
+	 *            The number of bytes written
+	 */
+	public void increaseReadDataBytes(int increment) {
+		if (wrappedSession instanceof AbstractIoSession) {
+			((AbstractIoSession) wrappedSession).increaseReadBytes(increment,
+					System.currentTimeMillis());
+		}
+	}
+
+	/**
+	 * Returns the last reply that was sent to the client.
+	 * 
+	 * @return the last reply that was sent to the client.
+	 */
+	public FtpReply getLastReply() {
+		return lastReply;
+	}
+
+	/**
+	 * @see IoSession#getWriteRequestQueue()
+	 */
+	public WriteRequestQueue getWriteRequestQueue() {
+		return wrappedSession.getWriteRequestQueue();
+	}
+
+	/**
+	 * @see IoSession#isReadSuspended()
+	 */
+	public boolean isReadSuspended() {
+		return wrappedSession.isReadSuspended();
+	}
+
+	/**
+	 * @see IoSession#isWriteSuspended()
+	 */
+	public boolean isWriteSuspended() {
+		return wrappedSession.isWriteSuspended();
+	}
+
+	/**
+	 * @see IoSession#setCurrentWriteRequest(WriteRequest)
+	 */
+	public void setCurrentWriteRequest(WriteRequest currentWriteRequest) {
+		wrappedSession.setCurrentWriteRequest(currentWriteRequest);
+	}
+
+	/**
+	 * @see IoSession#updateThroughput(long, boolean)
+	 */
+	public void updateThroughput(long currentTime, boolean force) {
+		wrappedSession.updateThroughput(currentTime, force);
+	}
 }

Modified: mina/ftpserver/branches/1.0.x/core/src/test/java/org/apache/ftpserver/clienttests/LoginTest.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/branches/1.0.x/core/src/test/java/org/apache/ftpserver/clienttests/LoginTest.java?rev=942689&r1=942688&r2=942689&view=diff
==============================================================================
--- mina/ftpserver/branches/1.0.x/core/src/test/java/org/apache/ftpserver/clienttests/LoginTest.java (original)
+++ mina/ftpserver/branches/1.0.x/core/src/test/java/org/apache/ftpserver/clienttests/LoginTest.java Mon May 10 08:58:54 2010
@@ -24,6 +24,7 @@ import java.io.ByteArrayInputStream;
 import org.apache.commons.net.ftp.FTPClient;
 import org.apache.commons.net.ftp.FTPConnectionClosedException;
 import org.apache.commons.net.ftp.FTPReply;
+import org.apache.ftpserver.ftplet.FtpStatistics;
 
 /**
 *
@@ -84,8 +85,8 @@ public class LoginTest extends ClientTes
     }
 
     public void testREIN() throws Exception {
-        assertTrue(client.login(ADMIN_USERNAME, ADMIN_PASSWORD));
-        assertTrue(FTPReply.isPositiveCompletion(client.rein()));
+    	assertTrue(client.login(ADMIN_USERNAME, ADMIN_PASSWORD));
+    	assertTrue(FTPReply.isPositiveCompletion(client.rein()));
         assertTrue(client.login(TESTUSER1_USERNAME, TESTUSER_PASSWORD));
     }
 
@@ -116,6 +117,15 @@ public class LoginTest extends ClientTes
     public void testLoginUnknownUser() throws Exception {
         assertFalse(client.login(UNKNOWN_USERNAME, UNKNOWN_PASSWORD));
     }
+    public void testLoginCount() throws Exception {
+    	FtpStatistics stats = server.getServerContext().getFtpStatistics();
+    	 assertTrue(client.login(ADMIN_USERNAME, ADMIN_PASSWORD));
+    	 int n =stats.getCurrentLoginNumber();
+    	 assertEquals(1,n );
+    	 client.rein();
+    	 client.logout();
+    	 assertEquals(0, stats.getCurrentLoginNumber());  	
+    }
 
     /*
      * public void testLoginWithMaxConnectionsPerIp() throws Exception {