001/* 002 * (C) Copyright 2006-2014 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 */ 020package org.nuxeo.ecm.core.convert.cache; 021 022import java.io.File; 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030import java.util.concurrent.locks.ReentrantReadWriteLock; 031 032import org.apache.commons.codec.binary.Base64; 033import org.apache.commons.logging.Log; 034import org.apache.commons.logging.LogFactory; 035import org.nuxeo.common.utils.Path; 036import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 037import org.nuxeo.ecm.core.convert.api.ConversionService; 038import org.nuxeo.ecm.core.convert.service.ConversionServiceImpl; 039 040/** 041 * Manager for the cache system of the {@link ConversionService}. 042 * 043 * @author tiry 044 */ 045public class ConversionCacheHolder { 046 047 protected static final Map<String, ConversionCacheEntry> cache = new HashMap<>(); 048 049 protected static final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock(); 050 051 private static final Log log = LogFactory.getLog(ConversionCacheHolder.class); 052 053 public static int nbSubPathPart = 5; 054 055 public static int subPathPartSize = 2; 056 057 public static long cacheHits = 0; 058 059 // Utility class. 060 private ConversionCacheHolder() { 061 } 062 063 public static long getCacheHits() { 064 return cacheHits; 065 } 066 067 public static int getNbCacheEntries() { 068 return cache.keySet().size(); 069 } 070 071 protected static List<String> getSubPathFromKey(String key) { 072 List<String> subPath = new ArrayList<>(); 073 074 String path = Base64.encodeBase64String(key.getBytes()); 075 076 path = path.replace("+", "X"); 077 path = path.replace("/", "Y"); 078 079 int idx = 0; 080 081 for (int i = 0; i < nbSubPathPart; i++) { 082 String subPart = path.substring(idx, idx + subPathPartSize); 083 subPath.add(subPart); 084 idx += subPathPartSize; 085 if (idx >= path.length()) { 086 break; 087 } 088 } 089 return subPath; 090 } 091 092 protected static String getCacheEntryPath(String key) { 093 Path path = new Path(ConversionServiceImpl.getCacheBasePath()); 094 095 List<String> subPath = getSubPathFromKey(key); 096 097 for (String subPart : subPath) { 098 path = path.append(subPart); 099 new File(path.toString()).mkdir(); 100 } 101 102 // path = path.append(key); 103 104 return path.toString(); 105 } 106 107 public static void addToCache(String key, BlobHolder result) { 108 cacheLock.writeLock().lock(); 109 try { 110 doAddToCache(key, result); 111 } finally { 112 cacheLock.writeLock().unlock(); 113 } 114 } 115 116 protected static void doAddToCache(String key, BlobHolder result) { 117 ConversionCacheEntry cce = new ConversionCacheEntry(result); 118 boolean persisted = false; 119 120 try { 121 persisted = cce.persist(getCacheEntryPath(key)); 122 } catch (IOException e) { 123 log.error("Error while trying to persist cache entry", e); 124 } 125 126 if (persisted) { 127 cache.put(key, cce); 128 } 129 } 130 131 public static void removeFromCache(String key) { 132 cacheLock.writeLock().lock(); 133 try { 134 doRemoveFromCache(key); 135 } finally { 136 cacheLock.writeLock().unlock(); 137 } 138 139 } 140 141 protected static void doRemoveFromCache(String key) { 142 if (cache.containsKey(key)) { 143 ConversionCacheEntry cce = cache.get(key); 144 cce.remove(); 145 cache.remove(key); 146 } 147 } 148 149 public static ConversionCacheEntry getCacheEntry(String key) { 150 cacheLock.readLock().lock(); 151 try { 152 return doGetCacheEntry(key); 153 } finally { 154 cacheLock.readLock().unlock(); 155 } 156 } 157 158 protected static ConversionCacheEntry doGetCacheEntry(String key) { 159 return cache.get(key); 160 } 161 162 public static BlobHolder getFromCache(String key) { 163 cacheLock.readLock().lock(); 164 try { 165 return doGetFromCache(key); 166 } finally { 167 cacheLock.readLock().unlock(); 168 } 169 } 170 171 protected static BlobHolder doGetFromCache(String key) { 172 ConversionCacheEntry cacheEntry = cache.get(key); 173 if (cacheEntry != null) { 174 if (cacheHits == Long.MAX_VALUE) { 175 cacheHits = 0; 176 } else { 177 cacheHits += 1; 178 } 179 return cacheEntry.restore(); 180 } 181 return null; 182 } 183 184 public static Set<String> getCacheKeys() { 185 cacheLock.readLock().lock(); 186 try { 187 return new HashSet<>(cache.keySet()); 188 } finally { 189 cacheLock.readLock().unlock(); 190 } 191 } 192 193 /** 194 * @since 6.0 195 */ 196 public static void deleteCache() { 197 cacheLock.writeLock().lock(); 198 try { 199 cache.clear(); 200 new File(ConversionServiceImpl.getCacheBasePath()).delete(); 201 } finally { 202 cacheLock.writeLock().unlock(); 203 } 204 } 205}