001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Nuxeo - initial API and implementation
011 *
012 * $Id$
013 *
014 */
015package org.nuxeo.ecm.core.convert.cache;
016
017import java.util.ArrayList;
018import java.util.Collections;
019import java.util.Date;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027import org.nuxeo.ecm.core.convert.service.ConversionServiceImpl;
028
029/**
030 * Manages GC processing to clean up disk cache.
031 *
032 * @author tiry
033 */
034public class ConversionCacheGCManager {
035
036    public static final String MAX_DISK_SPACE_USAGE_KEY = "MaxDiskSpaceUsageForCache";
037
038    public static final long MAX_DISK_SPACE_USAGE_KB = 1000;
039
040    private static final Log log = LogFactory.getLog(ConversionCacheGCManager.class);
041
042    private static int gcRuns = 0;
043
044    private static int gcCalls = 0;
045
046    // Utility class.
047    private ConversionCacheGCManager() {
048    }
049
050    protected static int getMaxDiskSpaceUsageKB() {
051        return ConversionServiceImpl.getMaxCacheSizeInKB();
052    }
053
054    public static int getGCRuns() {
055        return gcRuns;
056    }
057
058    public static int getGCCalls() {
059        return gcCalls;
060    }
061
062    public static long getCacheSizeInKB() {
063        long totalSize = 0;
064        Set<String> cacheKeys = ConversionCacheHolder.getCacheKeys();
065
066        for (String key : cacheKeys) {
067            ConversionCacheEntry cacheEntry = ConversionCacheHolder.getCacheEntry(key);
068            if (cacheEntry != null) {
069                totalSize += cacheEntry.getDiskSpaceUsageInKB();
070            }
071        }
072        return totalSize;
073    }
074
075    public static boolean gcIfNeeded() {
076        log.debug("GC Thread awake, see if there is some work to be done");
077
078        long totalSize = getCacheSizeInKB();
079        long limit = getMaxDiskSpaceUsageKB();
080
081        if (totalSize < limit) {
082            gcCalls += 1;
083            log.debug("No GC needed, go back to sleep for now");
084            return false;
085        }
086
087        // do the GC
088        long deltaInKB = totalSize - limit;
089        if (limit < 0) {
090            // mainly for testing : negative limit means cleanup everything
091            deltaInKB = totalSize;
092        }
093        log.debug("GC needed to free " + deltaInKB + " KB of data");
094        doGC(deltaInKB);
095        log.debug("GC terminated");
096        gcCalls += 1;
097        return true;
098    }
099
100    public static void doGC(long deltaInKB) {
101
102        Set<String> cacheKeys = ConversionCacheHolder.getCacheKeys();
103
104        Map<Date, String> sortingMap = new HashMap<Date, String>();
105
106        for (String key : cacheKeys) {
107            ConversionCacheEntry cacheEntry = ConversionCacheHolder.getCacheEntry(key);
108            if (key != null) {
109                sortingMap.put(cacheEntry.getLastAccessedTime(), key);
110            }
111        }
112
113        List<Date> accessTimeList = new ArrayList<Date>();
114        accessTimeList.addAll(sortingMap.keySet());
115        Collections.sort(accessTimeList);
116
117        long deletedVolume = 0;
118        for (Date accessDate : accessTimeList) {
119            ConversionCacheEntry cacheEntry = ConversionCacheHolder.getCacheEntry(sortingMap.get(accessDate));
120
121            long deletePotential = cacheEntry.getDiskSpaceUsageInKB();
122
123            deletedVolume += deletePotential;
124            ConversionCacheHolder.removeFromCache(sortingMap.get(accessDate));
125
126            if (deletedVolume > deltaInKB) {
127                break;
128            }
129        }
130        gcRuns += 1;
131    }
132
133}