001/*
002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 * $Id$
018 *
019 */
020package org.nuxeo.ecm.platform.pictures.tiles.service;
021
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.Date;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031
032/**
033 * Manage GC processing to clean up disk cache
034 *
035 * @author tiry
036 */
037public class PictureTilingCacheGCManager {
038
039    public static final String MAX_DISK_SPACE_USAGE_KEY = "MaxDiskSpaceUsageForCache";
040
041    public static final long MAX_DISK_SPACE_USAGE_KB = 1000;
042
043    private static final Log log = LogFactory.getLog(PictureTilingCacheGCManager.class);
044
045    private static int gcRuns = 0;
046
047    private static int gcCalls = 0;
048
049    protected static long getMaxDiskSpaceUsageKB() {
050        String maxStr = PictureTilingComponent.getEnvValue(MAX_DISK_SPACE_USAGE_KEY,
051                Long.toString(MAX_DISK_SPACE_USAGE_KB));
052        return Long.parseLong(maxStr);
053    }
054
055    public static int getGCRuns() {
056        return gcRuns;
057    }
058
059    public static int getGCCalls() {
060        return gcCalls;
061    }
062
063    public static long getCacheSizeInKBs() {
064        return getCacheSizeInBytes() / 1000;
065    }
066
067    public static long getCacheSizeInBytes() {
068        long totalSize = 0;
069        Map<String, PictureTilingCacheInfo> cache = PictureTilingComponent.getCache();
070
071        for (String key : cache.keySet()) {
072            PictureTilingCacheInfo cacheEntry = cache.get(key);
073            totalSize += cacheEntry.getDiskSpaceUsageInBytes();
074        }
075
076        return totalSize;
077    }
078
079    public static boolean gcIfNeeded() {
080        gcCalls += 1;
081        log.debug("GC Thread awake, see if there is some work to be done");
082
083        long totalSize = getCacheSizeInKBs();
084        long limit = getMaxDiskSpaceUsageKB();
085
086        if (totalSize < limit) {
087            log.debug("No GC needed, go back to sleep for now");
088            return false;
089        }
090
091        // do the GC
092        long deltaInKB = totalSize - limit;
093        log.debug("GC needed to free " + deltaInKB + " KB of data");
094        doGC(deltaInKB);
095        log.debug("GC terminated");
096
097        return true;
098    }
099
100    public static void doGC(long deltaInKB) {
101        gcRuns += 1;
102        Map<String, PictureTilingCacheInfo> cache = PictureTilingComponent.getCache();
103
104        Map<Date, String> sortingMap = new HashMap<Date, String>();
105
106        for (String key : cache.keySet()) {
107            PictureTilingCacheInfo cacheEntry = cache.get(key);
108            sortingMap.put(cacheEntry.getLastAccessedTime(), key);
109        }
110
111        List<Date> accesTimeList = new ArrayList<Date>();
112
113        accesTimeList.addAll(sortingMap.keySet());
114
115        Collections.sort(accesTimeList);
116
117        long deletedVolume = 0;
118        for (Date accessDate : accesTimeList) {
119            PictureTilingCacheInfo cacheEntry = cache.get(sortingMap.get(accessDate));
120
121            long deletePotential = cacheEntry.getDiskSpaceUsageInBytes() / (1024);
122
123            if (deletePotential > deltaInKB - deletedVolume) {
124                cacheEntry.partialCleanUp(deltaInKB - deletedVolume + 1);
125                return;
126            } else {
127                deletedVolume += deletePotential;
128                cacheEntry.cleanUp();
129                cache.remove(accessDate);
130            }
131            if (deletedVolume > deltaInKB)
132                break;
133        }
134    }
135
136}