1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.cache;
18
19 import java.lang.ref.Reference;
20 import java.lang.ref.ReferenceQueue;
21 import java.lang.ref.SoftReference;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.commons.vfs2.FileName;
29 import org.apache.commons.vfs2.FileObject;
30 import org.apache.commons.vfs2.FileSystem;
31
32
33
34
35
36
37
38 public class SoftRefFilesCache extends AbstractFilesCache {
39
40
41
42
43
44 private final class ReleaseThread extends Thread {
45 private ReleaseThread() {
46 setName(ReleaseThread.class.getName());
47 setDaemon(true);
48 }
49
50 @Override
51 public void run() {
52 try {
53 while (true) {
54 removeFile(refQueue.remove(0));
55 }
56 } catch (final InterruptedException e) {
57
58
59
60 }
61 }
62 }
63
64 private static final Log log = LogFactory.getLog(SoftRefFilesCache.class);
65 private final Map<FileSystem, Map<FileName, Reference<FileObject>>> fileSystemCache = new HashMap<>();
66 private final Map<Reference<FileObject>, FileSystemAndNameKey> refReverseMap = new HashMap<>(100);
67 private final ReferenceQueue<FileObject> refQueue = new ReferenceQueue<>();
68 private ReleaseThread releaseThread;
69
70
71
72
73 public SoftRefFilesCache() {
74
75 }
76
77 @Override
78 public synchronized void clear(final FileSystem fileSystem) {
79 final Map<FileName, Reference<FileObject>> files = getOrCreateFilesystemCache(fileSystem);
80 final Iterator<FileSystemAndNameKey> iterKeys = refReverseMap.values().iterator();
81
82 while (iterKeys.hasNext()) {
83 final FileSystemAndNameKey key = iterKeys.next();
84 if (key.getFileSystem() == fileSystem) {
85 iterKeys.remove();
86 files.remove(key.getFileName());
87 }
88 }
89
90 if (files.isEmpty()) {
91 close(fileSystem);
92 }
93 }
94
95 @Override
96 public synchronized void close() {
97 super.close();
98 endThread();
99 fileSystemCache.clear();
100 refReverseMap.clear();
101 }
102
103
104
105
106 private synchronized void close(final FileSystem fileSystem) {
107 if (log.isDebugEnabled()) {
108 log.debug("Close FileSystem: " + fileSystem.getRootName());
109 }
110
111 fileSystemCache.remove(fileSystem);
112 if (fileSystemCache.isEmpty()) {
113 endThread();
114 }
115 }
116
117 protected Reference<FileObject> createReference(final FileObject file, final ReferenceQueue<FileObject> refqueue) {
118 return new SoftReference<>(file, refqueue);
119 }
120
121 private synchronized void endThread() {
122 final ReleaseThread thread = releaseThread;
123 releaseThread = null;
124 if (thread != null) {
125 thread.interrupt();
126 }
127 }
128
129 @Override
130 public synchronized FileObject getFile(final FileSystem fileSystem, final FileName fileName) {
131 final Map<FileName, Reference<FileObject>> files = getOrCreateFilesystemCache(fileSystem);
132
133 final Reference<FileObject> ref = files.get(fileName);
134 if (ref == null) {
135 return null;
136 }
137
138 final FileObject fo = ref.get();
139 if (fo == null) {
140 removeFile(fileSystem, fileName);
141 }
142 return fo;
143 }
144
145 protected synchronized Map<FileName, Reference<FileObject>> getOrCreateFilesystemCache(final FileSystem fileSystem) {
146 if (fileSystemCache.isEmpty()) {
147 startThread();
148 }
149
150 return fileSystemCache.computeIfAbsent(fileSystem, k -> new HashMap<>());
151 }
152
153 private String getSafeName(final FileName fileName) {
154 return fileName.getFriendlyURI();
155 }
156
157 private String getSafeName(final FileObject fileObject) {
158 return this.getSafeName(fileObject.getName());
159 }
160
161 @Override
162 public void putFile(final FileObject fileObject) {
163 if (log.isDebugEnabled()) {
164 log.debug("putFile: " + this.getSafeName(fileObject));
165 }
166
167 synchronized(this) {
168 final Map<FileName, Reference<FileObject>> files = getOrCreateFilesystemCache(fileObject.getFileSystem());
169
170 final Reference<FileObject> ref = createReference(fileObject, refQueue);
171 final FileSystemAndNameKeystemAndNameKey.html#FileSystemAndNameKey">FileSystemAndNameKey key = new FileSystemAndNameKey(fileObject.getFileSystem(), fileObject.getName());
172
173 final Reference<FileObject> old = files.put(fileObject.getName(), ref);
174 if (old != null) {
175 refReverseMap.remove(old);
176 }
177 refReverseMap.put(ref, key);
178 }
179 }
180
181 @Override
182 public boolean putFileIfAbsent(final FileObject fileObject) {
183 if (log.isDebugEnabled()) {
184 log.debug("putFile: " + this.getSafeName(fileObject));
185 }
186
187 synchronized(this) {
188 final Map<FileName, Reference<FileObject>> files = getOrCreateFilesystemCache(fileObject.getFileSystem());
189
190 final Reference<FileObject> ref = createReference(fileObject, refQueue);
191 final FileSystemAndNameKeystemAndNameKey.html#FileSystemAndNameKey">FileSystemAndNameKey key = new FileSystemAndNameKey(fileObject.getFileSystem(), fileObject.getName());
192
193 if (files.containsKey(fileObject.getName()) && files.get(fileObject.getName()).get() != null) {
194 return false;
195 }
196 final Reference<FileObject> old = files.put(fileObject.getName(), ref);
197 if (old != null) {
198 refReverseMap.remove(old);
199 }
200 refReverseMap.put(ref, key);
201 return true;
202 }
203 }
204
205 @Override
206 public synchronized void removeFile(final FileSystem fileSystem, final FileName fileName) {
207 if (removeFile(new FileSystemAndNameKey(fileSystem, fileName))) {
208 close(fileSystem);
209 }
210 }
211
212 private synchronized boolean removeFile(final FileSystemAndNameKey key) {
213 if (log.isDebugEnabled()) {
214 log.debug("removeFile: " + this.getSafeName(key.getFileName()));
215 }
216
217 final Map<?, ?> files = getOrCreateFilesystemCache(key.getFileSystem());
218
219 final Object ref = files.remove(key.getFileName());
220 if (ref != null) {
221 refReverseMap.remove(ref);
222 }
223
224 return files.isEmpty();
225 }
226
227 private synchronized void removeFile(final Reference<?> ref) {
228 final FileSystemAndNameKey key = refReverseMap.get(ref);
229 if (key != null && removeFile(key)) {
230 close(key.getFileSystem());
231 }
232 }
233
234 private synchronized void startThread() {
235 if (releaseThread == null) {
236 releaseThread = new ReleaseThread();
237 releaseThread.start();
238
239
240
241 }
242 }
243
244 @Override
245 public String toString() {
246 return super.toString() + " [releaseThread=" + releaseThread
247 + (releaseThread == null ? "" : "(ID " + releaseThread.getId() + " is " + releaseThread.getState() + ")")
248 + "]";
249 }
250 }