You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by ng...@apache.org on 2008/06/25 16:24:54 UTC

svn commit: r671556 - /mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/FtpStatisticsImpl.java

Author: ngn
Date: Wed Jun 25 07:24:53 2008
New Revision: 671556

URL: http://svn.apache.org/viewvc?rev=671556&view=rev
Log:
Make FtpStatisticsImpl thread safe

Modified:
    mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/FtpStatisticsImpl.java

Modified: mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/FtpStatisticsImpl.java
URL: http://svn.apache.org/viewvc/mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/FtpStatisticsImpl.java?rev=671556&r1=671555&r2=671556&view=diff
==============================================================================
--- mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/FtpStatisticsImpl.java (original)
+++ mina/ftpserver/trunk/core/src/main/java/org/apache/ftpserver/FtpStatisticsImpl.java Wed Jun 25 07:24:53 2008
@@ -15,14 +15,17 @@
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
  * under the License.
- */  
+ */
 
 package org.apache.ftpserver;
 
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.util.Date;
-import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.ftpserver.ftplet.FileObject;
 import org.apache.ftpserver.ftplet.User;
@@ -32,370 +35,359 @@
 import org.apache.ftpserver.interfaces.StatisticsObserver;
 
 /**
- * This is ftp statistice implementation.
+ * This is FTP statistics implementation.
+ * 
+ * TODO revisit concurrency, right now we're a bit zealous with both Atomix* counters
+ * and synchronization
  */
 public class FtpStatisticsImpl implements ServerFtpStatistics {
 
     private StatisticsObserver observer = null;
-    private FileObserver fileObserver   = null;
-    
-    private Date startTime         = new Date();
-    
-    private int uploadCount        = 0;
-    private int downloadCount      = 0;
-    private int deleteCount        = 0;
-    
-    private int mkdirCount         = 0;
-    private int rmdirCount         = 0;
-    
-    private int currLogins         = 0;
-    private int totalLogins        = 0;
-    private int totalFailedLogins  = 0;
-    
-    private int currAnonLogins     = 0;
-    private int totalAnonLogins    = 0;
-    
-    private int currConnections    = 0;
-    private int totalConnections   = 0;
-    
-    private long bytesUpload       = 0L;
-    private long bytesDownload     = 0L;
-    
+    private FileObserver fileObserver = null;
+
+    private Date startTime = new Date();
+
+    private AtomicInteger uploadCount = new AtomicInteger(0);
+    private AtomicInteger downloadCount = new AtomicInteger(0);
+    private AtomicInteger deleteCount = new AtomicInteger(0);
+
+    private AtomicInteger mkdirCount = new AtomicInteger(0);
+    private AtomicInteger rmdirCount = new AtomicInteger(0);
+
+    private AtomicInteger currLogins = new AtomicInteger(0);
+    private AtomicInteger totalLogins = new AtomicInteger(0);
+    private AtomicInteger totalFailedLogins = new AtomicInteger(0);
+
+    private AtomicInteger currAnonLogins = new AtomicInteger(0);
+    private AtomicInteger totalAnonLogins = new AtomicInteger(0);
+
+    private AtomicInteger currConnections = new AtomicInteger(0);
+    private AtomicInteger totalConnections = new AtomicInteger(0);
+
+    private AtomicLong bytesUpload = new AtomicLong(0L);
+    private AtomicLong bytesDownload = new AtomicLong(0L);
+
+    private static class UserLogins {
+        private Map<InetAddress, AtomicInteger> perAddress = new ConcurrentHashMap<InetAddress, AtomicInteger>();
+
+        public UserLogins(InetAddress address) {
+            // init with the first connection
+            totalLogins = new AtomicInteger(1);
+            perAddress.put(address, new AtomicInteger(1));
+        }
+        
+        public AtomicInteger loginsFromInetAddress(InetAddress address) {
+            AtomicInteger logins = perAddress.get(address);
+            if (logins == null) {
+                logins = new AtomicInteger(0);
+                perAddress.put(address, logins);
+            }
+            return logins;
+        }
+
+        public AtomicInteger totalLogins;
+    }
+
     /**
-     *The user login information. The structure of the hashtable: 
-     * userLoginTable {user.getName ==> Hashtable {IP_address String ==> Login_Number}}
+     *The user login information.
      */
-    Hashtable<String, Hashtable<String, Integer>> userLoginTable = new Hashtable<String, Hashtable<String, Integer>>();
-    
+    private Map<String, UserLogins> userLoginTable = new ConcurrentHashMap<String, UserLogins>();
+
     public static final String LOGIN_NUMBER = "login_number";
-    
+
     /**
      * Set the observer.
      */
     public void setObserver(StatisticsObserver observer) {
         this.observer = observer;
     }
-    
+
     /**
      * Set the file observer.
      */
     public void setFileObserver(FileObserver observer) {
         fileObserver = observer;
     }
-    
-    
-    ////////////////////////////////////////////////////////
-    /////////////////  All getter methods  /////////////////
+
+    // //////////////////////////////////////////////////////
+    // /////////////// All getter methods /////////////////
     /**
      * Get server start time.
      */
     public Date getStartTime() {
         return (Date) startTime.clone();
     }
-     
+
     /**
      * Get number of files uploaded.
      */
     public int getTotalUploadNumber() {
-        return uploadCount;
+        return uploadCount.get();
     }
-    
+
     /**
      * Get number of files downloaded.
      */
     public int getTotalDownloadNumber() {
-        return downloadCount;
+        return downloadCount.get();
     }
-    
+
     /**
      * Get number of files deleted.
      */
     public int getTotalDeleteNumber() {
-        return deleteCount;
+        return deleteCount.get();
     }
-     
+
     /**
      * Get total number of bytes uploaded.
      */
     public long getTotalUploadSize() {
-        return bytesUpload;
+        return bytesUpload.get();
     }
-     
+
     /**
      * Get total number of bytes downloaded.
      */
     public long getTotalDownloadSize() {
-        return bytesDownload;
+        return bytesDownload.get();
     }
-    
+
     /**
      * Get total directory created.
      */
     public int getTotalDirectoryCreated() {
-        return mkdirCount;
+        return mkdirCount.get();
     }
-    
+
     /**
      * Get total directory removed.
      */
     public int getTotalDirectoryRemoved() {
-        return mkdirCount;
+        return mkdirCount.get();
     }
-    
+
     /**
      * Get total number of connections.
      */
     public int getTotalConnectionNumber() {
-        return totalConnections;
+        return totalConnections.get();
     }
-     
+
     /**
      * Get current number of connections.
      */
     public int getCurrentConnectionNumber() {
-        return currConnections;
+        return currConnections.get();
     }
-    
+
     /**
      * Get total number of logins.
      */
     public int getTotalLoginNumber() {
-        return totalLogins;
+        return totalLogins.get();
     }
-     
+
     /**
      * Get total failed login number.
      */
     public int getTotalFailedLoginNumber() {
-        return totalFailedLogins;
+        return totalFailedLogins.get();
     }
 
     /**
      * Get current number of logins.
      */
     public int getCurrentLoginNumber() {
-        return currLogins;
+        return currLogins.get();
     }
-    
+
     /**
      * Get total number of anonymous logins.
      */
     public int getTotalAnonymousLoginNumber() {
-        return totalAnonLogins;
+        return totalAnonLogins.get();
     }
-     
+
     /**
      * Get current number of anonymous logins.
      */
     public int getCurrentAnonymousLoginNumber() {
-        return currAnonLogins;
+        return currAnonLogins.get();
     }
-    
+
     /**
      * Get the login number for the specific user
      */
-    public int getCurrentUserLoginNumber(User user) {
-      Hashtable<String, Integer> statisticsTable = userLoginTable.get(user.getName());
-      if(statisticsTable == null){//not found the login user's statistics info
-        return 0;
-      } else{
-       Integer loginNumber = (Integer) statisticsTable.get(LOGIN_NUMBER);
-       if(loginNumber == null){
-         return 0;
-       } else{
-         return loginNumber.intValue();
-       }
-      }
+    public synchronized int getCurrentUserLoginNumber(User user) {
+        UserLogins userLogins = userLoginTable.get(user.getName());
+        if (userLogins == null) {// not found the login user's statistics info
+            return 0;
+        } else {
+            return userLogins.totalLogins.get();
+        }
     }
 
     /**
      * Get the login number for the specific user from the ipAddress
-     * @param user login user account
-     * @param ipAddress the ip address of the remote user
-     */
-    public int getCurrentUserLoginNumber(User user, InetAddress ipAddress) {
-      Hashtable<String, Integer> statisticsTable = userLoginTable.get(user.getName());
-      if(statisticsTable == null){//not found the login user's statistics info
-        return 0;
-      } else{
-        Integer loginNumber = (Integer) statisticsTable.get(ipAddress.getHostAddress());
-        if(loginNumber == null){
-          return 0;
-        } else{
-          return loginNumber.intValue();
-        }
-      }
-    }
-    
-    
-    ////////////////////////////////////////////////////////
-    /////////////////  All setter methods  /////////////////
+     * 
+     * @param user
+     *            login user account
+     * @param ipAddress
+     *            the ip address of the remote user
+     */
+    public synchronized int getCurrentUserLoginNumber(User user, InetAddress ipAddress) {
+        UserLogins userLogins = userLoginTable.get(user.getName());
+        if (userLogins == null) {// not found the login user's statistics info
+            return 0;
+        } else {
+            return userLogins.loginsFromInetAddress(ipAddress).get();
+        }
+    }
+
+    // //////////////////////////////////////////////////////
+    // /////////////// All setter methods /////////////////
     /**
      * Increment upload count.
      */
-    public void setUpload(FtpIoSession session, FileObject file, long size) {
-        ++uploadCount;
-        bytesUpload += size;
+    public synchronized void setUpload(FtpIoSession session, FileObject file, long size) {
+        uploadCount.incrementAndGet();
+        bytesUpload.addAndGet(size);
         notifyUpload(session, file, size);
     }
-     
+
     /**
      * Increment download count.
      */
-    public void setDownload(FtpIoSession session, FileObject file, long size) {
-        ++downloadCount;
-        bytesDownload += size;
+    public synchronized void setDownload(FtpIoSession session, FileObject file, long size) {
+        downloadCount.incrementAndGet();
+        bytesDownload.addAndGet(size);
         notifyDownload(session, file, size);
     }
-     
+
     /**
      * Increment delete count.
      */
-    public void setDelete(FtpIoSession session, FileObject file) {
-        ++deleteCount;
+    public synchronized void setDelete(FtpIoSession session, FileObject file) {
+        deleteCount.incrementAndGet();
         notifyDelete(session, file);
     }
-     
+
     /**
      * Increment make directory count.
      */
-    public void setMkdir(FtpIoSession session, FileObject file) {
-        ++mkdirCount;
+    public synchronized void setMkdir(FtpIoSession session, FileObject file) {
+        mkdirCount.incrementAndGet();
         notifyMkdir(session, file);
     }
-    
+
     /**
      * Increment remove directory count.
      */
-    public void setRmdir(FtpIoSession session, FileObject file) {
-        ++rmdirCount;
+    public synchronized void setRmdir(FtpIoSession session, FileObject file) {
+        rmdirCount.incrementAndGet();
         notifyRmdir(session, file);
     }
-    
+
     /**
      * Increment open connection count.
      */
-    public void setOpenConnection(FtpIoSession session) {
-        ++currConnections;
-        ++totalConnections;
+    public synchronized void setOpenConnection(FtpIoSession session) {
+        currConnections.incrementAndGet();
+        totalConnections.incrementAndGet();
         notifyOpenConnection(session);
     }
-    
+
     /**
      * Decrement open connection count.
      */
-    public void setCloseConnection(FtpIoSession session) {
-        if(currConnections > 0) {
-            --currConnections;
+    public synchronized void setCloseConnection(FtpIoSession session) {
+        if (currConnections.get() > 0) {
+            currConnections.decrementAndGet();
         }
         notifyCloseConnection(session);
     }
-    
+
     /**
      * New login.
      */
-    public void setLogin(FtpIoSession session) {
-        ++currLogins;
-        ++totalLogins;
+    public synchronized void setLogin(FtpIoSession session) {
+        currLogins.incrementAndGet();
+        totalLogins.incrementAndGet();
         User user = session.getUser();
-        if( "anonymous".equals(user.getName()) ) {
-            ++currAnonLogins;
-            ++totalAnonLogins;
-        }
-        
-        synchronized(user){//thread safety is needed. Since the login occurrs at low frequency, this overhead is endurable
-          Hashtable<String, Integer> statisticsTable = userLoginTable.get(user.getName());
-          if(statisticsTable == null){
-            //the hash table that records the login information of the user and its ip address.
-            //structure: IP_Address String ==> login_number
-            statisticsTable = new Hashtable<String, Integer>();
-            userLoginTable.put(user.getName(), statisticsTable);
-            //new login, put 1 in the login number
-            statisticsTable.put(LOGIN_NUMBER, new Integer(1));
-            if(session.getRemoteAddress() instanceof InetSocketAddress) {
-            	String address = ((InetSocketAddress)session.getRemoteAddress()).getAddress().getHostAddress();
-            	statisticsTable.put(address, new Integer(1));
-            }
-        } else{
-            Integer loginNumber = statisticsTable.get(LOGIN_NUMBER);
-            statisticsTable.put(LOGIN_NUMBER, new Integer(loginNumber.intValue() + 1));
-            
-            if(session.getRemoteAddress() instanceof InetSocketAddress) {
-            	String address = ((InetSocketAddress)session.getRemoteAddress()).getAddress().getHostAddress();
-            	Integer loginNumberPerIP = statisticsTable.get(address);
-
-            	if(loginNumberPerIP == null) {
-            		//new connection from this ip
-            		statisticsTable.put(address, new Integer(1));
-            	} else{//this ip has connections already
-            		statisticsTable.put(address, new Integer(loginNumberPerIP.intValue() + 1));
-            	}
+        if ("anonymous".equals(user.getName())) {
+            currAnonLogins.incrementAndGet();
+            totalAnonLogins.incrementAndGet();
+        }
+
+        synchronized (user) {// thread safety is needed. Since the login occurrs
+                             // at low frequency, this overhead is endurable
+            UserLogins statisticsTable = userLoginTable.get(user.getName());
+            if (statisticsTable == null) {
+                // the hash table that records the login information of the user
+                // and its ip address.
+                
+                InetAddress address = null;
+                if (session.getRemoteAddress() instanceof InetSocketAddress) {
+                    address = ((InetSocketAddress) session.getRemoteAddress()).getAddress();
+                }
+                statisticsTable = new UserLogins(address);
+                userLoginTable.put(user.getName(), statisticsTable);
+            } else {
+                statisticsTable.totalLogins.incrementAndGet();
+
+                if (session.getRemoteAddress() instanceof InetSocketAddress) {
+                    InetAddress address = ((InetSocketAddress) session.getRemoteAddress()).getAddress();
+                    statisticsTable.loginsFromInetAddress(address).incrementAndGet();
+                }
+
             }
-            
-          }
         }
-        
+
         notifyLogin(session);
     }
-    
+
     /**
      * Increment failed login count.
      */
-    public void setLoginFail(FtpIoSession session) {
-        ++totalFailedLogins;
+    public synchronized void setLoginFail(FtpIoSession session) {
+        totalFailedLogins.incrementAndGet();
         notifyLoginFail(session);
     }
-    
+
     /**
      * User logout
      */
-    public void setLogout(FtpIoSession session) {
+    public synchronized void setLogout(FtpIoSession session) {
         User user = session.getUser();
-        
-        if(user == null) {
-        	return;
+        if (user == null) {
+            return;
         }
 
-        --currLogins;
-        
-        if( "anonymous".equals(user.getName()) ) {
-            --currAnonLogins;
+        currLogins.decrementAndGet();
+
+        if ("anonymous".equals(user.getName())) {
+            currAnonLogins.decrementAndGet();
         }
-        
-        synchronized(user){
-          Hashtable<String, Integer> statisticsTable = userLoginTable.get(user.getName());
-          
-          if(statisticsTable != null) {
-	          Integer loginNumber = statisticsTable.get(LOGIN_NUMBER);
-	          statisticsTable.put(LOGIN_NUMBER, new Integer(loginNumber.intValue() - 1));
-          
-	          if(session.getRemoteAddress() instanceof InetSocketAddress) {
-	        	  String address = ((InetSocketAddress)session.getRemoteAddress()).getAddress().getHostAddress();
-	
-	        	  Integer loginNumberPerIP = statisticsTable.get(address);
-	        	  
-	        	  if(loginNumberPerIP != null){
-	        		  //this should always be true
-	        		  if(loginNumberPerIP.intValue() <= 1){
-	        			  //the last login from this ip, remove this ip address
-	        			  statisticsTable.remove(address);
-	        		  } else{
-	        			  //this ip has other logins, reduce the number
-	        			  statisticsTable.put(address, new Integer(loginNumberPerIP.intValue() - 1));
-	        		  }
-	        	  } 
-	          }
-          }          
-          
+
+        synchronized (user) {
+            UserLogins statisticsTable = userLoginTable.get(user.getName());
+
+            if (statisticsTable != null) {
+                statisticsTable.totalLogins.decrementAndGet();
+                if (session.getRemoteAddress() instanceof InetSocketAddress) {
+                    InetAddress address = ((InetSocketAddress) session.getRemoteAddress()).getAddress();
+                }
+            }
+
         }
-        
+
         notifyLogout(session);
     }
-    
-    
-    ////////////////////////////////////////////////////////////
-    ///////////////// all observer methods  ////////////////////
-    /**               
+
+    // //////////////////////////////////////////////////////////
+    // /////////////// all observer methods ////////////////////
+    /**
      * Observer upload notification.
      */
     private void notifyUpload(FtpIoSession session, FileObject file, long size) {
@@ -409,8 +401,8 @@
             fileObserver.notifyUpload(session, file, size);
         }
     }
-    
-    /**               
+
+    /**
      * Observer download notification.
      */
     private void notifyDownload(FtpIoSession session, FileObject file, long size) {
@@ -424,8 +416,8 @@
             fileObserver.notifyDownload(session, file, size);
         }
     }
-    
-    /**               
+
+    /**
      * Observer delete notification.
      */
     private void notifyDelete(FtpIoSession session, FileObject file) {
@@ -439,8 +431,8 @@
             fileObserver.notifyDelete(session, file);
         }
     }
-    
-    /**               
+
+    /**
      * Observer make directory notification.
      */
     private void notifyMkdir(FtpIoSession session, FileObject file) {
@@ -454,8 +446,8 @@
             fileObserver.notifyMkdir(session, file);
         }
     }
-    
-    /**               
+
+    /**
      * Observer remove directory notification.
      */
     private void notifyRmdir(FtpIoSession session, FileObject file) {
@@ -469,7 +461,7 @@
             fileObserver.notifyRmdir(session, file);
         }
     }
-    
+
     /**
      * Observer open connection notification.
      */
@@ -478,8 +470,8 @@
         if (observer != null) {
             observer.notifyOpenConnection();
         }
-    } 
-    
+    }
+
     /**
      * Observer close connection notification.
      */
@@ -488,39 +480,39 @@
         if (observer != null) {
             observer.notifyCloseConnection();
         }
-    } 
-    
+    }
+
     /**
      * Observer login notification.
      */
     private void notifyLogin(FtpIoSession session) {
         StatisticsObserver observer = this.observer;
         if (observer != null) {
-            
+
             // is anonymous login
             User user = session.getUser();
             boolean anonymous = false;
-            if(user != null) {
+            if (user != null) {
                 String login = user.getName();
                 anonymous = (login != null) && login.equals("anonymous");
             }
             observer.notifyLogin(anonymous);
         }
     }
-    
+
     /**
      * Observer failed login notification.
      */
     private void notifyLoginFail(FtpIoSession session) {
         StatisticsObserver observer = this.observer;
         if (observer != null) {
-        	if(session.getRemoteAddress() instanceof InetSocketAddress) {
-        		observer.notifyLoginFail(((InetSocketAddress)session.getRemoteAddress()).getAddress());
-        		
-        	}
+            if (session.getRemoteAddress() instanceof InetSocketAddress) {
+                observer.notifyLoginFail(((InetSocketAddress) session.getRemoteAddress()).getAddress());
+
+            }
         }
     }
-    
+
     /**
      * Observer logout notification.
      */
@@ -530,33 +522,33 @@
             // is anonymous login
             User user = session.getUser();
             boolean anonymous = false;
-            if(user != null) {
+            if (user != null) {
                 String login = user.getName();
                 anonymous = (login != null) && login.equals("anonymous");
             }
             observer.notifyLogout(anonymous);
         }
-    } 
+    }
 
     /**
      * Reset the cumulative counters.
      */
-    public void resetStatisticsCounters() {
+    public synchronized void resetStatisticsCounters() {
         startTime = new Date();
-        
-        uploadCount        = 0;
-        downloadCount      = 0;
-        deleteCount        = 0;
-        
-        mkdirCount         = 0;
-        rmdirCount         = 0;
-        
-        totalLogins        = 0;
-        totalFailedLogins  = 0;
-        totalAnonLogins    = 0;
-        totalConnections   = 0;
-        
-        bytesUpload        = 0;
-        bytesDownload      = 0;
+
+        uploadCount.set(0);
+        downloadCount.set(0);
+        deleteCount.set(0);
+
+        mkdirCount.set(0);
+        rmdirCount.set(0);
+
+        totalLogins.set(0);
+        totalFailedLogins.set(0);
+        totalAnonLogins.set(0);
+        totalConnections.set(0);
+
+        bytesUpload.set(0);
+        bytesDownload.set(0);
     }
 }