1 package com.atlassian.plugin.cache.filecache.impl;
2
3 import com.atlassian.plugin.cache.filecache.FileCache;
4 import com.atlassian.plugin.cache.filecache.FileCacheStreamProvider;
5 import com.atlassian.plugin.servlet.DownloadException;
6 import com.google.common.base.Function;
7 import com.google.common.collect.MapEvictionListener;
8 import com.google.common.collect.MapMaker;
9
10 import java.io.File;
11 import java.io.IOException;
12 import java.io.OutputStream;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.concurrent.ConcurrentMap;
16 import java.util.concurrent.atomic.AtomicLong;
17
18
19
20
21
22
23
24
25
26
27
28 public class FileCacheImpl<K> implements FileCache<K> {
29
30 static final String EXT = ".cachedfile";
31
32 private final ConcurrentMap<K, CachedFile> cache;
33 private final File tmpDir;
34 private final AtomicLong filenameCounter;
35
36
37
38
39
40
41 public FileCacheImpl(File tmpDir, int maxSize, AtomicLong filenameCounter) throws IOException {
42 if (maxSize < 0) {
43 throw new IllegalArgumentException("Max files can not be less than zero");
44 }
45 initDirectory(tmpDir);
46
47 this.tmpDir = tmpDir;
48
49 cache = new MapMaker()
50 .maximumSize(maxSize)
51 .evictionListener(new MapEvictionListener<K, CachedFile>() {
52 @Override
53 public void onEviction(K key, CachedFile value) {
54 FileCacheImpl.this.onEviction(value);
55 }
56 })
57 .makeComputingMap(new Function<K, CachedFile>() {
58 @Override
59 public CachedFile apply(K key) {
60 return newCachedFile();
61 }
62 });
63
64 this.filenameCounter = filenameCounter;
65 }
66
67 private static void initDirectory(File tmpDir) throws IOException {
68 tmpDir.mkdirs();
69 File[] files = tmpDir.listFiles();
70 if (files != null) {
71 for (File f : files) {
72 if (f.getName().toLowerCase().endsWith(EXT) && !f.delete()) {
73 throw new IOException("Could not delete existing cache file " + f);
74 }
75 }
76 }
77
78 if (!tmpDir.isDirectory()) {
79 throw new IOException("Could not create tmp directory " + tmpDir);
80 }
81 }
82
83 @Override
84 public void stream(K key, OutputStream dest, FileCacheStreamProvider input) throws DownloadException {
85 final CachedFile cachedFile = cache.get(key);
86
87
88 cachedFile.stream(dest, input);
89 }
90
91 @Override
92 public void clear() {
93
94
95
96
97 Collection<CachedFile> cachedFiles = new ArrayList<CachedFile>(cache.values());
98 cache.clear();
99
100 for (CachedFile cachedFile : cachedFiles) {
101 cachedFile.deleteWhenPossible();
102 }
103 }
104
105 private CachedFile newCachedFile() {
106 File file = new File(tmpDir, filenameCounter.incrementAndGet() + EXT);
107 return new CachedFile(file);
108 }
109
110 private void onEviction(CachedFile node) {
111 node.deleteWhenPossible();
112 }
113
114 }