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