Package cu.ftpd.user.statistics.local

Source Code of cu.ftpd.user.statistics.local.LocalUserStatistics

/**
* Copyright (c) 2007, Markus Jevring <markus@jevring.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The names of the contributors may not be used to endorse or promote
*    products derived from this software without specific prior written
*    permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/

package cu.ftpd.user.statistics.local;

import cu.ftpd.logging.Logging;
import cu.ftpd.user.User;
import cu.ftpd.user.statistics.StatisticsEntry;
import cu.ftpd.user.statistics.UserStatistics;
import cu.ftpd.user.userbases.NoSuchUserException;
import cu.ftpd.Server;
import cu.ftpd.ServiceManager;

import java.io.*;
import java.util.*;

/**
* @author Markus Jevring <markus@jevring.net>
* @since 2007-maj-28 : 21:36:29
* @version $Id: LocalUserStatistics.java 258 2008-10-26 12:47:23Z jevring $
*/
public class LocalUserStatistics implements UserStatistics {
    private final Map<String, StatisticsEntry> statistics = Collections.synchronizedMap(new HashMap<String, StatisticsEntry>());
    private final File userStatisticsDir;

    public LocalUserStatistics(File statisticsDir) {
        this.userStatisticsDir = statisticsDir;
      userStatisticsDir.mkdirs();
      if (!userStatisticsDir.isDirectory()) {
            throw new IllegalArgumentException("Could not find dir for user statistics: " + userStatisticsDir.getAbsolutePath() + " (or it is not a directory)");
        } else {
            for (String filename : userStatisticsDir.list()) {
                StatisticsEntry se = loadUserStatistics(filename);
                if (se != null) { // since we can return null, we can do a simple .isFile() check in the method
                    statistics.put(filename, se);
                }
            }
        }
    }

    public void upload(String username, String section, long bytes, long time) {
        StatisticsEntry us = getUserStatistics(username, section);
        us.upload(bytes, time);
        store(us);

        if (!"default".equals(section)) {
            // always add the data to the default section as well, since that keeps track of ALL the statistics
            us = getUserStatistics(username, "default");
            us.upload(bytes, time);
            store(us);
        }
    }

    public void download(String username, String section, long bytes, long time) {
        StatisticsEntry us = getUserStatistics(username, section);
        us.download(bytes, time);
        store(us);

        if (!"default".equals(section)) {
            // always add the data to the default section as well, since that keeps track of ALL the statistics
            us = getUserStatistics(username, "default");
            us.download(bytes, time);
            store(us);
        }
    }

    public void store(StatisticsEntry statisticsEntry) {
        // _todo: write lastUpdated to the file so we have sane data when we restart the server
        // used when a user logs off
        if (statisticsEntry != null) { // don't store things that do no exist
            PrintWriter out = null;
            try {
                //long start = System.currentTimeMillis();
                out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(userStatisticsDir, statisticsEntry.getName() + '.' + statisticsEntry.getSection())))));
                out.write(statisticsEntry.getLastUpdate() + "\r\n");
                out.write(statisticsEntry.toString());
                out.flush();
                //System.out.println((System.currentTimeMillis() - start) + " milliseconds taken to store userstatistics " + statisticsEntry.getName());
            } catch (IOException e) {
                Logging.getErrorLog().reportError("Failed to write user statistics to file: " + e.getMessage());
            } finally {
                if (out != null) {
                    out.close();
                }
            }
        }
    }

    private StatisticsEntry loadUserStatistics(String filename){
        final File statfile = new File(userStatisticsDir, filename);
        if (!statfile.isFile()) {
            // this is ok, since we have other reasons for returning null
            return null;
        }
        String[] s = filename.split("\\.");
        if (s.length != 2) {
            // some weird file, skip it (this also means that usernames or sections cannon contain periods)
            return null;
            // _todo: let it be possible for sections to contain periods, so this check must be done in a different manner
            // sections can no longer contain periods
        }
        StatisticsEntry us = new StatisticsEntry(s[0], s[1]);
        //long start = System.currentTimeMillis();
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(new FileInputStream(statfile)));
            String line = in.readLine();
            try {
                us.setLastUpdate(Long.parseLong(line));
                line = in.readLine();
            } catch (NumberFormatException e) {
                // do nothing
                // we get this error if we convert from old-style files that did not have this date at the top
            }
            line = line.substring(1, line.length() - 1); // remove the wrapping "[" and "]"
            String[] statline = line.split(",\\s+");
            for (int i = 0; i < statline.length; i++) {
                try {
                    us.set(i, Long.parseLong(statline[i]));
                } catch (NumberFormatException e) {
                    System.err.println("Found bad data in user statistics file: " + statline[i] + " for user: " + filename);
                }
            }
            statistics.put(filename, us);
        } catch (IOException e) {
            Logging.getErrorLog().reportError("Failed to read user statistics from file: " + e.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //System.out.println((System.currentTimeMillis() - start) + " milliseconds taken to read userstatistics " + username);
        return us;
    }

    public StatisticsEntry getUserStatistics(String username, String section) {
        // _todo: handle the case of the default section
        // no problem, the default section is named "default", so there will always be a name
        // _todo: we must handle this for extracting statistics as well, also, when we add stuff to a section, we also always need to add it to the default section
        // done (see download() and upload())
        //System.out.println("requesting statistics entry: " + username + "." + section);
        StatisticsEntry us = statistics.get(username + '.' + section);
        if (us == null) {
            //System.out.println("entry not found, creating...");
            us = new StatisticsEntry(username, section);
            statistics.put(username + '.' + section, us);
        }
        return us;
    }

    public TreeMap<Long, StatisticsEntry> get(int statistics, String section) {
        TreeMap<Long, StatisticsEntry> map = new TreeMap<Long, StatisticsEntry>();
        for (StatisticsEntry se : this.statistics.values()) {
            if (se.getSection().equals(section)) {
                // we're doing this as an easy way of flipping the ordering of the TreeMap, as I didn't want to write an inverse comparator for such a simple thing
                map.put(Long.MAX_VALUE - se.get(statistics), se);
            }
        }
        return map;
    }

    /**
     * Returns the statistics for the groups. If a groupname is supplied, the statistics represent only members of that group.
     *
     * @param groupname the group for which to view user statistics. <code>null</code> creates a list for all groups.
     * @param statistics the statistics we are after.
     * @param section the name of the section
     * @return if a group name was specified; a TreeMap with statistics inside the named group. If no name was provided; a TreeMap with statistics of all the groups, collected and sorted per group.
     */
    public TreeMap<Long, StatisticsEntry> getGroupStatistics(String groupname, int statistics, String section) {
        // _todo: support using sections here (maybe in the future)
        // not yet, too much work, and nobody will use if. if the users want it, I will add it.
        TreeMap<Long, StatisticsEntry> map = new TreeMap<Long, StatisticsEntry>();
        if (groupname == null || "default".equals(groupname)) {
            // we are doing a ranking for all groupS
            HashMap<String, StatisticsEntry> groupStats = createGroupStats(section);
            for (StatisticsEntry se : groupStats.values()) {
                map.put(Long.MAX_VALUE - se.get(UserStatistics.ALLUP_BYTES), se);
            }
        } else {
            // we are doing a ranking WITHIN A group
            for (StatisticsEntry us : this.statistics.values()) {
                try {
                    if (ServiceManager.getServices().getUserbase().getUser(us.getName()).isMemberOfGroup(groupname) && us.getSection().equals(section)) {
                        // we're doing this as an easy way of flipping the ordering of the TreeMap, as I didn't want to write an inverse comparator for such a simple thing
                        map.put(Long.MAX_VALUE - us.get(UserStatistics.ALLUP_BYTES), us);
                    }
                } catch (NoSuchUserException e) {
                    System.err.println("Found some user statistics that belongs to an unknown user: " + e.getMessage()); // this can happen if a user has recently been removed from the userbase, but his statistics are still here.
                }
            }
        }
        return map;
    }
   
    private HashMap<String, StatisticsEntry> createGroupStats(String section) {
        // NOTE: for now, it broke when passing a section, so we will just disregard the setting section
        // we will come back to it later
        if (section == null || "".equals(section)) {
            section = "default";
        }
        HashMap<String, StatisticsEntry> groupStats = new HashMap<String, StatisticsEntry>();
        for (User user : ServiceManager.getServices().getUserbase().getUsers().values()) {
            // note: this only lists groups that have members in them. otherwise they won't have any stats anyway
            for (String group : user.getGroups()) {
                StatisticsEntry stat = groupStats.get(group);
                if (stat == null) {
                    stat = new StatisticsEntry(group, section);
                    groupStats.put(group, stat);
                }
                // _todo: support using sections here (maybe in the future)
                // not yet, too much work, and nobody will use if. if the users want it, I will add it.
                stat.set(UserStatistics.ALLUP_BYTES, (stat.get(UserStatistics.ALLUP_BYTES) + getUserStatistics(user.getUsername(), section).get(UserStatistics.ALLUP_BYTES)));
                stat.set(UserStatistics.ALLDN_BYTES, (stat.get(UserStatistics.ALLDN_BYTES) + getUserStatistics(user.getUsername(), section).get(UserStatistics.ALLDN_BYTES)));
                stat.set(UserStatistics.MNUP_BYTES, (stat.get(UserStatistics.MNUP_BYTES) + getUserStatistics(user.getUsername(), section).get(UserStatistics.MNUP_BYTES)));
                stat.set(UserStatistics.MNDN_BYTES, (stat.get(UserStatistics.MNDN_BYTES) + getUserStatistics(user.getUsername(), section).get(UserStatistics.MNDN_BYTES)));
                stat.set(UserStatistics.WKUP_BYTES, (stat.get(UserStatistics.WKUP_BYTES) + getUserStatistics(user.getUsername(), section).get(UserStatistics.WKUP_BYTES)));
                stat.set(UserStatistics.WKDN_BYTES, (stat.get(UserStatistics.WKDN_BYTES) + getUserStatistics(user.getUsername(), section).get(UserStatistics.WKDN_BYTES)));
                stat.set(UserStatistics.DAYUP_BYTES, (stat.get(UserStatistics.DAYUP_BYTES) + getUserStatistics(user.getUsername(), section).get(UserStatistics.DAYUP_BYTES)));
                stat.set(UserStatistics.DAYDN_BYTES, (stat.get(UserStatistics.DAYDN_BYTES) + getUserStatistics(user.getUsername(), section).get(UserStatistics.DAYDN_BYTES)));
            }
        }
        return groupStats;
    }

    public void deleteUser(String username) {
        synchronized(statistics) {
            Iterator<Map.Entry<String,StatisticsEntry>> i = statistics.entrySet().iterator();
            while (i.hasNext()) {
                Map.Entry<String,StatisticsEntry> entry = i.next();
                if (entry.getValue().getName().equals(username)) {
                    i.remove();
                    File statfile = new File(userStatisticsDir, entry.getKey());
                    statfile.delete();
                }
            }
        }
    }
}
TOP

Related Classes of cu.ftpd.user.statistics.local.LocalUserStatistics

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.