001/*
002 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 *
021 */
022package org.nuxeo.ecm.platform.pictures.tiles.service;
023
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.Date;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033
034/**
035 * Manage GC processing to clean up disk cache
036 *
037 * @author tiry
038 */
039public class PictureTilingCacheGCManager {
040
041    public static final String MAX_DISK_SPACE_USAGE_KEY = "MaxDiskSpaceUsageForCache";
042
043    public static final long MAX_DISK_SPACE_USAGE_KB = 1000;
044
045    private static final Log log = LogFactory.getLog(PictureTilingCacheGCManager.class);
046
047    private static int gcRuns = 0;
048
049    private static int gcCalls = 0;
050
051    protected static long getMaxDiskSpaceUsageKB() {
052        String maxStr = PictureTilingComponent.getEnvValue(MAX_DISK_SPACE_USAGE_KEY,
053                Long.toString(MAX_DISK_SPACE_USAGE_KB));
054        return Long.parseLong(maxStr);
055    }
056
057    public static int getGCRuns() {
058        return gcRuns;
059    }
060
061    public static int getGCCalls() {
062        return gcCalls;
063    }
064
065    public static long getCacheSizeInKBs() {
066        return getCacheSizeInBytes() / 1000;
067    }
068
069    public static long getCacheSizeInBytes() {
070        long totalSize = 0;
071        Map<String, PictureTilingCacheInfo> cache = PictureTilingComponent.getCache();
072
073        for (String key : cache.keySet()) {
074            PictureTilingCacheInfo cacheEntry = cache.get(key);
075            totalSize += cacheEntry.getDiskSpaceUsageInBytes();
076        }
077
078        return totalSize;
079    }
080
081    public static boolean gcIfNeeded() {
082        gcCalls += 1;
083        log.debug("GC Thread awake, see if there is some work to be done");
084
085        long totalSize = getCacheSizeInKBs();
086        long limit = getMaxDiskSpaceUsageKB();
087
088        if (totalSize < limit) {
089            log.debug("No GC needed, go back to sleep for now");
090            return false;
091        }
092
093        // do the GC
094        long deltaInKB = totalSize - limit;
095        log.debug("GC needed to free " + deltaInKB + " KB of data");
096        doGC(deltaInKB);
097        log.debug("GC terminated");
098
099        return true;
100    }
101
102    public static void doGC(long deltaInKB) {
103        gcRuns += 1;
104        Map<String, PictureTilingCacheInfo> cache = PictureTilingComponent.getCache();
105
106        Map<Date, String> sortingMap = new HashMap<Date, String>();
107
108        for (String key : cache.keySet()) {
109            PictureTilingCacheInfo cacheEntry = cache.get(key);
110            sortingMap.put(cacheEntry.getLastAccessedTime(), key);
111        }
112
113        List<Date> accesTimeList = new ArrayList<Date>();
114
115        accesTimeList.addAll(sortingMap.keySet());
116
117        Collections.sort(accesTimeList);
118
119        long deletedVolume = 0;
120        for (Date accessDate : accesTimeList) {
121            PictureTilingCacheInfo cacheEntry = cache.get(sortingMap.get(accessDate));
122
123            long deletePotential = cacheEntry.getDiskSpaceUsageInBytes() / (1024);
124
125            if (deletePotential > deltaInKB - deletedVolume) {
126                cacheEntry.partialCleanUp(deltaInKB - deletedVolume + 1);
127                return;
128            } else {
129                deletedVolume += deletePotential;
130                cacheEntry.cleanUp();
131                cache.remove(accessDate);
132            }
133            if (deletedVolume > deltaInKB)
134                break;
135        }
136    }
137
138}