1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs2.provider;
18
19 import java.io.File;
20 import java.lang.reflect.InvocationTargetException;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.concurrent.atomic.AtomicInteger;
28 import java.util.concurrent.atomic.AtomicLong;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.commons.vfs2.CacheStrategy;
33 import org.apache.commons.vfs2.Capability;
34 import org.apache.commons.vfs2.FileListener;
35 import org.apache.commons.vfs2.FileName;
36 import org.apache.commons.vfs2.FileObject;
37 import org.apache.commons.vfs2.FileSelector;
38 import org.apache.commons.vfs2.FileSystem;
39 import org.apache.commons.vfs2.FileSystemConfigBuilder;
40 import org.apache.commons.vfs2.FileSystemException;
41 import org.apache.commons.vfs2.FileSystemManager;
42 import org.apache.commons.vfs2.FileSystemOptions;
43 import org.apache.commons.vfs2.FilesCache;
44 import org.apache.commons.vfs2.VfsLog;
45 import org.apache.commons.vfs2.cache.OnCallRefreshFileObject;
46 import org.apache.commons.vfs2.events.AbstractFileChangeEvent;
47 import org.apache.commons.vfs2.events.ChangedEvent;
48 import org.apache.commons.vfs2.events.CreateEvent;
49 import org.apache.commons.vfs2.events.DeleteEvent;
50 import org.apache.commons.vfs2.impl.DefaultFileSystemConfigBuilder;
51 import org.apache.commons.vfs2.util.FileObjectUtils;
52 import org.apache.commons.vfs2.util.Messages;
53
54
55
56
57 public abstract class AbstractFileSystem extends AbstractVfsComponent implements FileSystem {
58
59 private static final FileListener[] EMPTY_FILE_LISTENER_ARRAY = {};
60
61 private static final Log LOG = LogFactory.getLog(AbstractFileSystem.class);
62
63
64
65
66 private final FileName rootName;
67
68
69
70
71
72 private final String rootURI;
73
74 private final Collection<Capability> capabilities = new HashSet<>();
75
76 private final FileObject parentLayer;
77
78
79
80
81 private final Map<FileName, ArrayList<FileListener>> listenerMap = new HashMap<>();
82
83
84
85
86 private final FileSystemOptions fileSystemOptions;
87
88
89
90
91 private final AtomicLong useCount = new AtomicLong();
92
93 private FileSystemKey cacheKey;
94
95
96
97
98 private final AtomicInteger openStreams = new AtomicInteger();
99
100
101
102
103 AbstractFileSystem() {
104 this(null, null, null);
105 }
106
107
108
109
110
111
112
113
114 protected AbstractFileSystem(final FileName rootName, final FileObject parentLayer, final FileSystemOptions fileSystemOptions) {
115 this.parentLayer = parentLayer;
116 this.rootName = rootName;
117 this.fileSystemOptions = fileSystemOptions;
118 final FileSystemConfigBuilder builder = DefaultFileSystemConfigBuilder.getInstance();
119 String uri = builder.getRootURI(fileSystemOptions);
120 if (uri == null) {
121 uri = rootName != null ? rootName.getURI() : null;
122 }
123 this.rootURI = uri;
124 }
125
126
127
128
129
130
131 protected abstract void addCapabilities(Collection<Capability> caps);
132
133
134
135
136
137
138
139
140 @Override
141 public void addJunction(final String junctionPoint, final FileObject targetFile) throws FileSystemException {
142 throw new FileSystemException("vfs.provider/junctions-not-supported.error", rootName);
143 }
144
145
146
147
148
149
150
151 @Override
152 public void addListener(final FileObject file, final FileListener listener) {
153 synchronized (listenerMap) {
154 final ArrayList<FileListener> listeners = listenerMap.computeIfAbsent(file.getName(), k -> new ArrayList<>());
155 listeners.add(listener);
156 }
157 }
158
159
160
161
162 @Override
163 public void close() {
164 closeCommunicationLink();
165 }
166
167
168
169
170 public void closeCommunicationLink() {
171 synchronized (this) {
172 doCloseCommunicationLink();
173 }
174 }
175
176
177
178
179
180
181
182
183
184
185
186 protected abstract FileObject createFile(AbstractFileName name) throws Exception;
187
188
189
190
191
192
193
194
195 protected FileObject decorateFileObject(FileObject file) throws FileSystemException {
196 if (getFileSystemManager().getCacheStrategy().equals(CacheStrategy.ON_CALL)) {
197 file = new OnCallRefreshFileObject(file);
198 }
199
200 if (getFileSystemManager().getFileObjectDecoratorConst() != null) {
201 try {
202 file = (FileObject) getFileSystemManager().getFileObjectDecoratorConst().newInstance(file);
203 } catch (final InstantiationException | IllegalAccessException | InvocationTargetException e) {
204 throw new FileSystemException("vfs.impl/invalid-decorator.error",
205 getFileSystemManager().getFileObjectDecorator().getName(), e);
206 }
207 }
208
209 return file;
210 }
211
212
213
214
215 protected void doCloseCommunicationLink() {
216
217 }
218
219
220
221
222
223
224
225
226
227 protected File doReplicateFile(final FileObject file, final FileSelector selector) throws Exception {
228 return getContext().getReplicator().replicateFile(file, selector);
229 }
230
231 void fileObjectDestroyed(final FileObject fileObject) {
232 useCount.decrementAndGet();
233 }
234
235 void fileObjectHanded(final FileObject fileObject) {
236 useCount.incrementAndGet();
237 }
238
239
240
241
242 private void fireEvent(final AbstractFileChangeEvent event) {
243 FileListener[] fileListeners = null;
244 final FileObject fileObject = event.getFileObject();
245
246 synchronized (listenerMap) {
247 final ArrayList<?> listeners = listenerMap.get(fileObject.getName());
248 if (listeners != null) {
249 fileListeners = listeners.toArray(EMPTY_FILE_LISTENER_ARRAY);
250 }
251 }
252
253 if (fileListeners != null) {
254 for (final FileListener fileListener : fileListeners) {
255 try {
256 event.notify(fileListener);
257 } catch (final Exception e) {
258 final String message = Messages.getString("vfs.provider/notify-listener.warn", fileObject);
259
260 VfsLog.warn(getLogger(), LOG, message, e);
261 }
262 }
263 }
264 }
265
266
267
268
269
270
271
272
273
274 public void fireFileChanged(final FileObject file) {
275 fireEvent(new ChangedEvent(file));
276 }
277
278
279
280
281
282
283 public void fireFileCreated(final FileObject file) {
284 fireEvent(new CreateEvent(file));
285 }
286
287
288
289
290
291
292 public void fireFileDeleted(final FileObject file) {
293 fireEvent(new DeleteEvent(file));
294 }
295
296 void freeResources() {
297
298 }
299
300
301
302
303
304
305
306
307 @Override
308 public Object getAttribute(final String attrName) throws FileSystemException {
309 throw new FileSystemException("vfs.provider/get-attribute-not-supported.error");
310 }
311
312 FileSystemKey getCacheKey() {
313 return cacheKey;
314 }
315
316
317
318
319
320
321
322 protected FileObject getFileFromCache(final FileName name) {
323 return getFilesCache().getFile(this, name);
324 }
325
326 private FilesCache getFilesCache() {
327 final FilesCache filesCache = getContext().getFileSystemManager().getFilesCache();
328 return Objects.requireNonNull(filesCache, () -> Messages.getString("vfs.provider/files-cache-missing.error"));
329 }
330
331
332
333
334
335
336 @Override
337 public FileSystemManager getFileSystemManager() {
338 return getContext().getFileSystemManager();
339 }
340
341
342
343
344
345
346 @Override
347 public FileSystemOptions getFileSystemOptions() {
348 return fileSystemOptions;
349 }
350
351
352
353
354
355
356 @Override
357 public double getLastModTimeAccuracy() {
358 return 0;
359 }
360
361
362
363
364
365
366
367 @Override
368 public FileObject getParentLayer() throws FileSystemException {
369 return parentLayer;
370 }
371
372
373
374
375
376
377
378 @Override
379 public FileObject getRoot() throws FileSystemException {
380 return resolveFile(rootName);
381 }
382
383
384
385
386
387
388 @Override
389 public FileName getRootName() {
390 return rootName;
391 }
392
393
394
395
396
397
398
399 @Override
400 public String getRootURI() {
401 return rootURI;
402 }
403
404
405
406
407
408
409
410 @Override
411 public boolean hasCapability(final Capability capability) {
412 return capabilities.contains(capability);
413 }
414
415
416
417
418
419
420 @Override
421 public void init() throws FileSystemException {
422 addCapabilities(capabilities);
423 }
424
425
426
427
428
429
430 public boolean isOpen() {
431 return openStreams.get() > 0;
432 }
433
434
435
436
437
438
439 public boolean isReleaseable() {
440 return useCount.get() < 1;
441 }
442
443
444
445
446 protected void notifyAllStreamsClosed() {
447
448 }
449
450
451
452
453
454
455 protected void putFileToCache(final FileObject file) {
456 getFilesCache().putFile(file);
457 }
458
459
460
461
462
463
464 protected void removeFileFromCache(final FileName name) {
465 getFilesCache().removeFile(this, name);
466 }
467
468
469
470
471
472
473
474 @Override
475 public void removeJunction(final String junctionPoint) throws FileSystemException {
476 throw new FileSystemException("vfs.provider/junctions-not-supported.error", rootName);
477 }
478
479
480
481
482
483
484
485 @Override
486 public void removeListener(final FileObject file, final FileListener listener) {
487 synchronized (listenerMap) {
488 final ArrayList<?> listeners = listenerMap.get(file.getName());
489 if (listeners != null) {
490 listeners.remove(listener);
491 if (listeners.isEmpty()) {
492 listenerMap.remove(file.getName());
493 }
494 }
495 }
496 }
497
498
499
500
501
502
503
504
505
506 @Override
507 public File replicateFile(final FileObject file, final FileSelector selector) throws FileSystemException {
508 if (!FileObjectUtils.exists(file)) {
509 throw new FileSystemException("vfs.provider/replicate-missing-file.error", file.getName());
510 }
511
512 try {
513 return doReplicateFile(file, selector);
514 } catch (final Exception e) {
515 throw new FileSystemException("vfs.provider/replicate-file.error", file.getName(), e);
516 }
517 }
518
519
520
521
522
523
524
525
526 @Override
527 public FileObject resolveFile(final FileName name) throws FileSystemException {
528 return resolveFile(name, true);
529 }
530
531 private synchronized FileObject resolveFile(final FileName name, final boolean useCache)
532 throws FileSystemException {
533 if (!rootName.getRootURI().equals(name.getRootURI())) {
534 throw new FileSystemException("vfs.provider/mismatched-fs-for-name.error", name, rootName,
535 name.getRootURI());
536 }
537
538
539 FileObject file;
540 if (useCache) {
541 file = getFileFromCache(name);
542 } else {
543 file = null;
544 }
545
546 if (file == null) {
547 try {
548 file = createFile((AbstractFileName) name);
549 } catch (final Exception e) {
550 throw new FileSystemException("vfs.provider/resolve-file.error", name, e);
551 }
552
553 file = decorateFileObject(file);
554
555
556 if (useCache) {
557 putFileToCache(file);
558 }
559 }
560
561
562
563
564 if (getFileSystemManager().getCacheStrategy().equals(CacheStrategy.ON_RESOLVE)) {
565 file.refresh();
566 }
567 return file;
568 }
569
570
571
572
573
574
575
576
577 @Override
578 public FileObject resolveFile(final String nameStr) throws FileSystemException {
579
580 return resolveFile(getFileSystemManager().resolveName(rootName, nameStr));
581 }
582
583
584
585
586
587
588
589
590 @Override
591 public void setAttribute(final String attrName, final Object value) throws FileSystemException {
592 throw new FileSystemException("vfs.provider/set-attribute-not-supported.error");
593 }
594
595 void setCacheKey(final FileSystemKey cacheKey) {
596 this.cacheKey = cacheKey;
597 }
598
599 void streamClosed() {
600 if (openStreams.decrementAndGet() == 0) {
601 notifyAllStreamsClosed();
602 }
603 }
604
605 void streamOpened() {
606 openStreams.incrementAndGet();
607 }
608 }