/* * StatisticsLoader.java * * Version: $Revision: 3734 $ * * Date: $Date: 2009-04-24 04:00:19 +0000 (Fri, 24 Apr 2009) $ * * Copyright (c) 2002-2009, The DSpace Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - 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. * * - Neither the name of the DSpace Foundation nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT * HOLDERS 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 org.dspace.app.statistics; import org.apache.commons.lang.time.DateUtils; import org.dspace.core.ConfigurationManager; import java.util.*; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.io.File; import java.io.FilenameFilter; import java.text.SimpleDateFormat; import java.text.ParseException; /** * Helper class for loading the analysis / report files from the reports directory */ public class StatisticsLoader { private static Map<String, StatsFile> monthlyAnalysis = null; private static Map<String, StatsFile> monthlyReports = null; private static StatsFile generalAnalysis = null; private static StatsFile generalReport = null; private static Date lastLoaded = null; private static int fileCount = 0; private static Pattern analysisMonthlyPattern; private static Pattern analysisGeneralPattern; private static Pattern reportMonthlyPattern; private static Pattern reportGeneralPattern; private static SimpleDateFormat monthlySDF; private static SimpleDateFormat generalSDF; // one time initialisation of the regex patterns and formatters we will use static { analysisMonthlyPattern = Pattern.compile("dspace-log-monthly-([0-9][0-9][0-9][0-9]-[0-9]+)\\.dat"); analysisGeneralPattern = Pattern.compile("dspace-log-general-([0-9]+-[0-9]+-[0-9]+)\\.dat"); reportMonthlyPattern = Pattern.compile("report-([0-9][0-9][0-9][0-9]-[0-9]+)\\.html"); reportGeneralPattern = Pattern.compile("report-general-([0-9]+-[0-9]+-[0-9]+)\\.html"); monthlySDF = new SimpleDateFormat("yyyy'-'M"); generalSDF = new SimpleDateFormat("yyyy'-'M'-'dd"); } /** * Get an array of the dates of the report files * @return */ public static Date[] getMonthlyReportDates() { return sortDatesDescending(getDatesFromMap(monthlyReports)); } /** * Get an array of the dates of the analysis files * @return */ public static Date[] getMonthlyAnalysisDates() { return sortDatesDescending(getDatesFromMap(monthlyAnalysis)); } /** * Convert the formatted dates that are the keys of the map into a date array * @param monthlyMap * @return */ protected static Date[] getDatesFromMap(Map<String, StatsFile> monthlyMap) { Set<String> keys = monthlyMap.keySet(); Date[] dates = new Date[keys.size()]; int i = 0; for (String date : keys) { try { dates[i] = monthlySDF.parse(date); } catch (ParseException pe) { } i++; } return dates; } /** * Sort the date array in descending (reverse chronological) order * @param dates * @return */ protected static Date[] sortDatesDescending(Date[] dates) { Arrays.sort(dates, new Comparator<Date>() { SimpleDateFormat sdf = monthlySDF; public int compare(Date d1, Date d2) { if (d1 == null && d2 == null) return 0; else if (d1 == null) return -1; else if (d2 == null) return 1; else if (d1.before(d2)) return 1; else if (d2.before(d1)) return -1; return 0; } }); return dates; } /** * Get the analysis file for a given date * @param date * @return */ public static File getAnalysisFor(String date) { StatisticsLoader.syncFileList(); StatsFile sf = (monthlyAnalysis == null ? null : monthlyAnalysis.get(date)); return sf == null ? null : sf.file; } /** * Get the report file for a given date * @param date * @return */ public static File getReportFor(String date) { StatisticsLoader.syncFileList(); StatsFile sf = (monthlyReports == null ? null : monthlyReports.get(date)); return sf == null ? null : sf.file; } /** * Get the current general analysis file * @return */ public static File getGeneralAnalysis() { StatisticsLoader.syncFileList(); return generalAnalysis == null ? null : generalAnalysis.file; } /** * Get the current general report file * @return */ public static File getGeneralReport() { StatisticsLoader.syncFileList(); return generalReport == null ? null : generalReport.file; } /** * Syncronize the cached list of analysis / report files with the reports directory * * We synchronize if: * * 1) The number of files is different (ie. files have been added or removed) * 2) We haven't cached anything yet * 3) The cache was last generate over an hour ago */ private static void syncFileList() { // Get an array of all the analysis and report files present File[] fileList = StatisticsLoader.getAnalysisAndReportFileList(); if (fileList != null && fileList.length != fileCount) StatisticsLoader.loadFileList(fileList); else if (lastLoaded == null) StatisticsLoader.loadFileList(fileList); else if (DateUtils.addHours(lastLoaded, 1).before(new Date())) StatisticsLoader.loadFileList(fileList); } /** * Generate the cached file list from the array of files * @param fileList */ private static synchronized void loadFileList(File[] fileList) { // If we haven't been passed an array of files, get one now if (fileList == null || fileList.length == 0) { fileList = StatisticsLoader.getAnalysisAndReportFileList(); } // Create new maps for the monthly analyis / reports Map<String, StatsFile> newMonthlyAnalysis = new HashMap<String, StatsFile>(); Map<String, StatsFile> newMonthlyReports = new HashMap<String, StatsFile>(); StatsFile newGeneralAnalysis = null; StatsFile newGeneralReport = null; if (fileList != null) { for (File thisFile : fileList) { StatsFile statsFile = null; // If we haven't identified this file yet if (statsFile == null) { // See if it is a monthly analysis file statsFile = makeStatsFile(thisFile, analysisMonthlyPattern, monthlySDF); if (statsFile != null) { // If it is, add it to the map newMonthlyAnalysis.put(statsFile.dateStr, statsFile); } } // If we haven't identified this file yet if (statsFile == null) { // See if it is a monthly report file statsFile = makeStatsFile(thisFile, reportMonthlyPattern, monthlySDF); if (statsFile != null) { // If it is, add it to the map newMonthlyReports.put(statsFile.dateStr, statsFile); } } // If we haven't identified this file yet if (statsFile == null) { // See if it is a general analysis file statsFile = makeStatsFile(thisFile, analysisGeneralPattern, generalSDF); if (statsFile != null) { // If it is, ensure that we are pointing to the most recent file if (newGeneralAnalysis == null || statsFile.date.after(newGeneralAnalysis.date)) { newGeneralAnalysis = statsFile; } } } // If we haven't identified this file yet if (statsFile == null) { // See if it is a general report file statsFile = makeStatsFile(thisFile, reportGeneralPattern, generalSDF); if (statsFile != null) { // If it is, ensure that we are pointing to the most recent file if (newGeneralReport == null || statsFile.date.after(newGeneralReport.date)) { newGeneralReport = statsFile; } } } } } // Store the newly discovered values in the member cache monthlyAnalysis = newMonthlyAnalysis; monthlyReports = newMonthlyReports; generalAnalysis = newGeneralAnalysis; generalReport = newGeneralReport; lastLoaded = new Date(); } /** * Generate a StatsFile entry for this file. The pattern and date formatters are used to * identify the file as a particular type, and extract the relevant information. * If the file is not identified by the formatter provided, then we return null * @param thisFile * @param thisPattern * @param sdf * @return */ private static StatsFile makeStatsFile(File thisFile, Pattern thisPattern, SimpleDateFormat sdf) { Matcher matcher = thisPattern.matcher(thisFile.getName()); if (matcher.matches()) { StatsFile sf = new StatsFile(); sf.file = thisFile; sf.path = thisFile.getPath(); sf.dateStr = matcher.group(1).trim(); try { sf.date = sdf.parse(sf.dateStr); } catch (ParseException e) { } return sf; } return null; } /** * Get an array of all the analysis and report files * @return */ private static File[] getAnalysisAndReportFileList() { File reportDir = new File(ConfigurationManager.getProperty("log.dir")); if (reportDir != null) { return reportDir.listFiles(new AnalysisAndReportFilter()); } return null; } /** * Simple class for holding information about an analysis/report file */ private static class StatsFile { File file; String path; Date date; String dateStr; } /** * Filter used to restrict files in the reports directory to just analysis or report types */ private static class AnalysisAndReportFilter implements FilenameFilter { public boolean accept(File dir, String name) { if (analysisMonthlyPattern.matcher(name).matches()) return true; if (analysisGeneralPattern.matcher(name).matches()) return true; if (reportMonthlyPattern.matcher(name).matches()) return true; if (reportGeneralPattern.matcher(name).matches()) return true; return false; } } }