package com.kodcu.service.cache; import com.kodcu.other.Current; import com.kodcu.other.IOHelper; import com.kodcu.service.ThreadService; import javafx.application.Platform; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Collection; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; /** * Created by usta on 12.06.2016. */ @Component public class BinaryCacheService { private final long maximumSize = 50 * 1024 * 1024; private final AtomicLong totalSize = new AtomicLong(0); private final ConcurrentHashMap<String, CacheData> cache = new ConcurrentHashMap<>(); private final ThreadService threadService; private final Current current; private Logger logger = LoggerFactory.getLogger(BinaryCacheService.class); @Autowired public BinaryCacheService(ThreadService threadService, Current current) { this.threadService = threadService; this.current = current; } public String putBinary(String key, byte[] bytes) { if (Platform.isFxApplicationThread()) { threadService.runTaskLater(() -> { putBinary(key, bytes); }); return key; } synchronized (this) { if (hasCacheFor(bytes)) { saveInMemory(key, bytes); } else { saveInDisk(key, bytes); } return key; } } private void enLargeCache() { threadService.runTaskLater(() -> { Collection<CacheData> values = cache.values(); logger.debug("Enlarge cache: {}", values.size()); values .stream() .filter(e -> e.inDisk()) .sorted((o1, o2) -> { Long l1 = o1.lastModified(); Long l2 = o2.lastModified(); return l1.compareTo(l2); }).limit(5) .forEach(cacheData -> { logger.debug("Larged: {}", cacheData.key()); saveInMemory(cacheData.key(), cacheData.readBytes()); }); }); } private void shrinkCache() { threadService.runTaskLater(() -> { Collection<CacheData> values = cache.values(); logger.debug("Shrink cache: {}", values.size()); values .stream() .filter(e -> e.inMemory()) .sorted((o1, o2) -> { Long l1 = o1.lastModified(); Long l2 = o2.lastModified(); return l2.compareTo(l1); }).limit(5) .forEach(cacheData -> { logger.debug("Shrinked: {}", cacheData.key()); byte[] bytes = cacheData.readBytes(); saveInDisk(cacheData.key(), bytes); totalSize.addAndGet(-bytes.length); }); }); } private void saveInDisk(String key, byte[] bytes) { threadService.runActionLater(() -> { Path tempFile = IOHelper.createTempFile(current.currentPath().get().getParent(), ".png"); // System.out.println(tempFile.getParent()); IOHelper.writeToFile(tempFile, bytes, StandardOpenOption.CREATE); Optional.ofNullable(cache.get(key)) .ifPresent(CacheData::removeFromDisk); cache.put(key, new InDiskData(key, tempFile)); }); } private void saveInMemory(String key, byte[] bytes) { Optional.ofNullable(cache.get(key)) .ifPresent(CacheData::removeFromDisk); Optional.ofNullable(cache.get(key)) .filter(e->e.inMemory()) .map(e->e.length()) .ifPresent(aLong -> totalSize.addAndGet(-aLong)); cache.put(key, new InMemoryDAta(key, bytes)); long length = totalSize.addAndGet(bytes.length); } private boolean hasCacheFor(byte[] bytes) { return maximumSize > (getTotalSize() + bytes.length); } private long getTotalSize() { return totalSize.get(); } public CacheData getCacheData(String key) { return cache.get(key); } public void putBinary(String key, BufferedImage trimmed) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();) { ImageIO.write(trimmed, "png", outputStream); byte[] bytes = outputStream.toByteArray(); putBinary(key, bytes); } catch (IOException e) { e.printStackTrace(); } } public boolean hasCache(String key) { return cache.containsKey(key); } }