1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs.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 import java.util.TreeMap;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.commons.vfs.FileName;
30 import org.apache.commons.vfs.FileObject;
31 import org.apache.commons.vfs.FileSystem;
32 import org.apache.commons.vfs.VfsLog;
33 import org.apache.commons.vfs.impl.DefaultFileSystemManager;
34 import org.apache.commons.vfs.util.Messages;
35
36 /***
37 * This implementation caches every file as long as it is strongly reachable by
38 * the java vm. As soon as the vm needs memory - every softly reachable file
39 * will be discarded.
40 *
41 * @author <a href="mailto:imario@apache.org">Mario Ivankovits</a>
42 * @version $Revision: 484648 $ $Date: 2005-09-30 09:02:41 +0200 (Fr, 30 Sep
43 * 2005) $
44 * @see SoftReference
45 */
46 public class SoftRefFilesCache extends AbstractFilesCache
47 {
48 /***
49 * The logger to use.
50 */
51 private Log log = LogFactory.getLog(SoftRefFilesCache.class);
52
53 private final Map filesystemCache = new HashMap();
54 private final Map refReverseMap = new HashMap(100);
55 private final ReferenceQueue refqueue = new ReferenceQueue();
56
57 private SoftRefReleaseThread softRefReleaseThread = null;
58
59 /***
60 * This thread will listen on the ReferenceQueue and remove the entry in the
61 * filescache as soon as the vm removes the reference
62 */
63 private class SoftRefReleaseThread extends Thread
64 {
65 private boolean requestEnd = false;
66
67 private SoftRefReleaseThread()
68 {
69 setName(SoftRefReleaseThread.class.getName());
70 setDaemon(true);
71 }
72
73 public void run()
74 {
75 loop: while (!requestEnd && !Thread.currentThread().isInterrupted())
76 {
77 try
78 {
79 Reference ref = refqueue.remove(1000);
80 if (ref == null)
81 {
82 continue;
83 }
84
85 FileSystemAndNameKey key = (FileSystemAndNameKey) refReverseMap
86 .get(ref);
87
88 if (key != null)
89 {
90 if (removeFile(key))
91 {
92 filesystemClose(key.getFileSystem());
93 }
94 }
95 }
96 catch (InterruptedException e)
97 {
98 if (!requestEnd)
99 {
100 VfsLog
101 .warn(
102 getLogger(),
103 log,
104 Messages
105 .getString("vfs.impl/SoftRefReleaseThread-interrupt.info"));
106 }
107 break loop;
108 }
109 }
110 }
111 }
112
113 public SoftRefFilesCache()
114 {
115 }
116
117 private void startThread()
118 {
119 if (softRefReleaseThread != null)
120 {
121 throw new IllegalStateException(
122 Messages
123 .getString("vfs.impl/SoftRefReleaseThread-already-running.warn"));
124 }
125
126 softRefReleaseThread = new SoftRefReleaseThread();
127 softRefReleaseThread.start();
128 }
129
130 private void endThread()
131 {
132 if (softRefReleaseThread != null)
133 {
134 softRefReleaseThread.requestEnd = true;
135 softRefReleaseThread.interrupt();
136 softRefReleaseThread = null;
137 }
138 }
139
140 public void putFile(final FileObject file)
141 {
142 if (log.isDebugEnabled())
143 {
144 log.debug("putFile: " + file.getName());
145 }
146
147 Map files = getOrCreateFilesystemCache(file.getFileSystem());
148
149 Reference ref = createReference(file, refqueue);
150 FileSystemAndNameKey key = new FileSystemAndNameKey(file
151 .getFileSystem(), file.getName());
152
153 synchronized (files)
154 {
155 files.put(file.getName(), ref);
156 refReverseMap.put(ref, key);
157 }
158 }
159
160 protected Reference createReference(FileObject file, ReferenceQueue refqueue)
161 {
162 return new SoftReference(file, refqueue);
163 }
164
165 public FileObject getFile(final FileSystem filesystem, final FileName name)
166 {
167 Map files = getOrCreateFilesystemCache(filesystem);
168
169 synchronized (files)
170 {
171 Reference ref = (Reference) files.get(name);
172 if (ref == null)
173 {
174 return null;
175 }
176
177 FileObject fo = (FileObject) ref.get();
178 if (fo == null)
179 {
180 removeFile(filesystem, name);
181 }
182 return fo;
183 }
184 }
185
186 public void clear(FileSystem filesystem)
187 {
188 Map files = getOrCreateFilesystemCache(filesystem);
189
190 boolean closeFilesystem;
191
192 synchronized (files)
193 {
194 Iterator iterKeys = refReverseMap.values().iterator();
195 while (iterKeys.hasNext())
196 {
197 FileSystemAndNameKey key = (FileSystemAndNameKey) iterKeys
198 .next();
199 if (key.getFileSystem() == filesystem)
200 {
201 iterKeys.remove();
202 files.remove(key.getFileName());
203 }
204 }
205
206 closeFilesystem = files.size() < 1;
207 }
208
209 if (closeFilesystem)
210 {
211 filesystemClose(filesystem);
212 }
213 }
214
215 private void filesystemClose(FileSystem filesystem)
216 {
217 if (log.isDebugEnabled())
218 {
219 log.debug("close fs: " + filesystem.getRootName());
220 }
221 synchronized (filesystemCache)
222 {
223 filesystemCache.remove(filesystem);
224 if (filesystemCache.size() < 1)
225 {
226 endThread();
227 }
228 }
229 ((DefaultFileSystemManager) getContext().getFileSystemManager())
230 ._closeFileSystem(filesystem);
231 }
232
233 public void close()
234 {
235 super.close();
236
237 endThread();
238
239
240 synchronized (filesystemCache)
241 {
242 filesystemCache.clear();
243 }
244 refReverseMap.clear();
245 }
246
247 public void removeFile(FileSystem filesystem, FileName name)
248 {
249 if (removeFile(new FileSystemAndNameKey(filesystem, name)))
250 {
251 filesystemClose(filesystem);
252 }
253 }
254
255 public void touchFile(FileObject file)
256 {
257 }
258
259 private boolean removeFile(final FileSystemAndNameKey key)
260 {
261 if (log.isDebugEnabled())
262 {
263 log.debug("removeFile: " + key.getFileName());
264 }
265
266 Map files = getOrCreateFilesystemCache(key.getFileSystem());
267
268 synchronized (files)
269 {
270 Object ref = files.remove(key.getFileName());
271 if (ref != null)
272 {
273 refReverseMap.remove(ref);
274 }
275
276 return files.size() < 1;
277 }
278 }
279
280 protected Map getOrCreateFilesystemCache(final FileSystem filesystem)
281 {
282 synchronized (filesystemCache)
283 {
284 if (filesystemCache.size() < 1)
285 {
286 startThread();
287 }
288
289 Map files = (Map) filesystemCache.get(filesystem);
290 if (files == null)
291 {
292 files = new TreeMap();
293 filesystemCache.put(filesystem, files);
294 }
295
296 return files;
297 }
298 }
299 }