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);
}
}