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}