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