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