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;
033import org.nuxeo.ecm.platform.pictures.tiles.api.PictureTilingService;
034import org.nuxeo.runtime.api.Framework;
035
036/**
037 * Manage GC processing to clean up disk cache
038 *
039 * @author tiry
040 */
041public class PictureTilingCacheGCManager {
042
043    public static final String MAX_DISK_SPACE_USAGE_KEY = "MaxDiskSpaceUsageForCache";
044
045    public static final long MAX_DISK_SPACE_USAGE_KB = 1000;
046
047    private static final Log log = LogFactory.getLog(PictureTilingCacheGCManager.class);
048
049    private static int gcRuns = 0;
050
051    private static int gcCalls = 0;
052
053    protected static long getMaxDiskSpaceUsageKB() {
054        PictureTilingComponent ptc = (PictureTilingComponent) Framework.getService(PictureTilingService.class);
055        String maxStr = ptc.getEnvValue(MAX_DISK_SPACE_USAGE_KEY, Long.toString(MAX_DISK_SPACE_USAGE_KB));
056        return Long.parseLong(maxStr);
057    }
058
059    public static int getGCRuns() {
060        return gcRuns;
061    }
062
063    public static int getGCCalls() {
064        return gcCalls;
065    }
066
067    public static long getCacheSizeInKBs() {
068        return getCacheSizeInBytes() / 1000;
069    }
070
071    public static long getCacheSizeInBytes() {
072        long totalSize = 0;
073        PictureTilingComponent ptc = (PictureTilingComponent) Framework.getService(PictureTilingService.class);
074        Map<String, PictureTilingCacheInfo> cache = ptc.getCache();
075
076        for (String key : cache.keySet()) {
077            PictureTilingCacheInfo cacheEntry = cache.get(key);
078            totalSize += cacheEntry.getDiskSpaceUsageInBytes();
079        }
080
081        return totalSize;
082    }
083
084    public static boolean gcIfNeeded() {
085        gcCalls += 1;
086        log.debug("GC Thread awake, see if there is some work to be done");
087
088        long totalSize = getCacheSizeInKBs();
089        long limit = getMaxDiskSpaceUsageKB();
090
091        if (totalSize < limit) {
092            log.debug("No GC needed, go back to sleep for now");
093            return false;
094        }
095
096        // do the GC
097        long deltaInKB = totalSize - limit;
098        log.debug("GC needed to free " + deltaInKB + " KB of data");
099        doGC(deltaInKB);
100        log.debug("GC terminated");
101
102        return true;
103    }
104
105    public static void doGC(long deltaInKB) {
106        gcRuns += 1;
107        PictureTilingComponent ptc = (PictureTilingComponent) Framework.getService(PictureTilingService.class);
108        Map<String, PictureTilingCacheInfo> cache = ptc.getCache();
109
110        Map<Date, String> sortingMap = new HashMap<Date, String>();
111
112        for (String key : cache.keySet()) {
113            PictureTilingCacheInfo cacheEntry = cache.get(key);
114            sortingMap.put(cacheEntry.getLastAccessedTime(), key);
115        }
116
117        List<Date> accesTimeList = new ArrayList<Date>();
118
119        accesTimeList.addAll(sortingMap.keySet());
120
121        Collections.sort(accesTimeList);
122
123        long deletedVolume = 0;
124        for (Date accessDate : accesTimeList) {
125            String key = sortingMap.get(accessDate);
126            PictureTilingCacheInfo cacheEntry = cache.get(key);
127
128            long deletePotential = cacheEntry.getDiskSpaceUsageInBytes() / (1024);
129
130            if (deletePotential > deltaInKB - deletedVolume) {
131                cacheEntry.partialCleanUp(deltaInKB - deletedVolume + 1);
132                return;
133            } else {
134                deletedVolume += deletePotential;
135                cacheEntry.cleanUp();
136                cache.remove(key);
137            }
138            if (deletedVolume > deltaInKB)
139                break;
140        }
141    }
142
143}