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.util.Map;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ConcurrentMap;
22 import java.util.concurrent.locks.Lock;
23 import java.util.concurrent.locks.ReadWriteLock;
24 import java.util.concurrent.locks.ReentrantReadWriteLock;
25
26 import org.apache.commons.collections4.map.AbstractLinkedMap;
27 import org.apache.commons.collections4.map.LRUMap;
28 import org.apache.commons.io.IOUtils;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.commons.vfs2.FileName;
32 import org.apache.commons.vfs2.FileObject;
33 import org.apache.commons.vfs2.FileSystem;
34 import org.apache.commons.vfs2.VfsLog;
35 import org.apache.commons.vfs2.util.Messages;
36
37
38
39
40
41
42
43 public class LRUFilesCache extends AbstractFilesCache {
44
45
46
47
48 private class MyLRUMap extends LRUMap<FileName, FileObject> {
49
50
51
52 private static final long serialVersionUID = 20101208L;
53
54
55 private final FileSystem filesystem;
56
57 MyLRUMap(final FileSystem filesystem, final int size) {
58 super(size, true);
59 this.filesystem = filesystem;
60 }
61
62 @Override
63 protected boolean removeLRU(final AbstractLinkedMap.LinkEntry<FileName, FileObject> linkEntry) {
64 synchronized (LRUFilesCache.this) {
65 @SuppressWarnings("resource")
66 final FileObject fileObject = linkEntry.getValue();
67
68
69
70 if (fileObject.isAttached() || fileObject.isContentOpen()) {
71
72
73
74
75 return false;
76 }
77
78
79 if (super.removeLRU(linkEntry)) {
80
81 IOUtils.closeQuietly(fileObject, e -> VfsLog.warn(getLogger(), log, Messages.getString("vfs.impl/LRUFilesCache-remove-ex.warn"), e));
82 final Map<?, ?> files = fileSystemCache.get(filesystem);
83 if (files.isEmpty()) {
84 fileSystemCache.remove(filesystem);
85 }
86 return true;
87 }
88
89 return false;
90 }
91 }
92 }
93
94
95 private static final int DEFAULT_LRU_SIZE = 100;
96
97
98 private static final Log log = LogFactory.getLog(LRUFilesCache.class);
99
100
101 private final ConcurrentMap<FileSystem, Map<FileName, FileObject>> fileSystemCache = new ConcurrentHashMap<>();
102
103
104 private final int lruSize;
105 private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
106
107
108
109
110 public LRUFilesCache() {
111 this(DEFAULT_LRU_SIZE);
112 }
113
114
115
116
117
118
119 public LRUFilesCache(final int lruSize) {
120 this.lruSize = lruSize;
121 }
122
123 @Override
124 public void clear(final FileSystem filesystem) {
125 final Map<FileName, FileObject> files = getOrCreateFilesystemCache(filesystem);
126
127 writeLock().lock();
128 try {
129 files.clear();
130
131 fileSystemCache.remove(filesystem);
132 } finally {
133 writeLock().unlock();
134 }
135 }
136
137 @Override
138 public void close() {
139 super.close();
140 fileSystemCache.clear();
141 }
142
143 @Override
144 public FileObject getFile(final FileSystem filesystem, final FileName name) {
145 final Map<FileName, FileObject> files = getOrCreateFilesystemCache(filesystem);
146 readLock().lock();
147 try {
148 return files.get(name);
149 } finally {
150 readLock().unlock();
151 }
152 }
153
154
155
156
157
158
159
160 protected Map<FileName, FileObject> getOrCreateFilesystemCache(final FileSystem fileSystem) {
161 return fileSystemCache.computeIfAbsent(fileSystem, k -> new MyLRUMap(k, lruSize));
162 }
163
164 @Override
165 public void putFile(final FileObject file) {
166 final Map<FileName, FileObject> files = getOrCreateFilesystemCache(file.getFileSystem());
167
168 writeLock().lock();
169 try {
170 files.put(file.getName(), file);
171 } finally {
172 writeLock().unlock();
173 }
174 }
175
176 @Override
177 public boolean putFileIfAbsent(final FileObject file) {
178 final Map<FileName, FileObject> files = getOrCreateFilesystemCache(file.getFileSystem());
179
180 writeLock().lock();
181 try {
182 return files.putIfAbsent(file.getName(), file) == null;
183 } finally {
184 writeLock().unlock();
185 }
186 }
187
188 private Lock readLock() {
189 return rwLock.readLock();
190 }
191
192 @Override
193 public void removeFile(final FileSystem filesystem, final FileName name) {
194 final Map<?, ?> files = getOrCreateFilesystemCache(filesystem);
195
196 writeLock().lock();
197 try {
198 files.remove(name);
199
200 if (files.isEmpty()) {
201 fileSystemCache.remove(filesystem);
202 }
203 } finally {
204 writeLock().unlock();
205 }
206 }
207
208 @Override
209 public void touchFile(final FileObject file) {
210
211 getFile(file.getFileSystem(), file.getName());
212 }
213
214 private Lock writeLock() {
215 return rwLock.writeLock();
216 }
217 }