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