001/*
002 * (C) Copyright 2006-2011 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.core.convert.cache;
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;
030import java.util.Set;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.nuxeo.ecm.core.convert.service.ConversionServiceImpl;
035
036/**
037 * Manages GC processing to clean up disk cache.
038 *
039 * @author tiry
040 */
041public class ConversionCacheGCManager {
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(ConversionCacheGCManager.class);
048
049    private static int gcRuns = 0;
050
051    private static int gcCalls = 0;
052
053    // Utility class.
054    private ConversionCacheGCManager() {
055    }
056
057    protected static int getMaxDiskSpaceUsageKB() {
058        return ConversionServiceImpl.getMaxCacheSizeInKB();
059    }
060
061    public static int getGCRuns() {
062        return gcRuns;
063    }
064
065    public static int getGCCalls() {
066        return gcCalls;
067    }
068
069    public static long getCacheSizeInKB() {
070        long totalSize = 0;
071        Set<String> cacheKeys = ConversionCacheHolder.getCacheKeys();
072
073        for (String key : cacheKeys) {
074            ConversionCacheEntry cacheEntry = ConversionCacheHolder.getCacheEntry(key);
075            if (cacheEntry != null) {
076                totalSize += cacheEntry.getDiskSpaceUsageInKB();
077            }
078        }
079        return totalSize;
080    }
081
082    public static boolean gcIfNeeded() {
083        log.debug("GC Thread awake, see if there is some work to be done");
084
085        long totalSize = getCacheSizeInKB();
086        long limit = getMaxDiskSpaceUsageKB();
087
088        if (totalSize < limit) {
089            gcCalls += 1;
090            log.debug("No GC needed, go back to sleep for now");
091            return false;
092        }
093
094        // do the GC
095        long deltaInKB = totalSize - limit;
096        if (limit < 0) {
097            // mainly for testing : negative limit means cleanup everything
098            deltaInKB = totalSize;
099        }
100        log.debug("GC needed to free " + deltaInKB + " KB of data");
101        doGC(deltaInKB);
102        log.debug("GC terminated");
103        gcCalls += 1;
104        return true;
105    }
106
107    public static void doGC(long deltaInKB) {
108
109        Set<String> cacheKeys = ConversionCacheHolder.getCacheKeys();
110
111        Map<Date, String> sortingMap = new HashMap<Date, String>();
112
113        for (String key : cacheKeys) {
114            ConversionCacheEntry cacheEntry = ConversionCacheHolder.getCacheEntry(key);
115            if (key != null) {
116                sortingMap.put(cacheEntry.getLastAccessedTime(), key);
117            }
118        }
119
120        List<Date> accessTimeList = new ArrayList<Date>();
121        accessTimeList.addAll(sortingMap.keySet());
122        Collections.sort(accessTimeList);
123
124        long deletedVolume = 0;
125        for (Date accessDate : accessTimeList) {
126            ConversionCacheEntry cacheEntry = ConversionCacheHolder.getCacheEntry(sortingMap.get(accessDate));
127
128            long deletePotential = cacheEntry.getDiskSpaceUsageInKB();
129
130            deletedVolume += deletePotential;
131            ConversionCacheHolder.removeFromCache(sortingMap.get(accessDate));
132
133            if (deletedVolume > deltaInKB) {
134                break;
135            }
136        }
137        gcRuns += 1;
138    }
139
140}