View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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      // private FileObject root;
63      private final Collection caps = new HashSet();
64  
65      /***
66       * Map from FileName to FileObject.
67       */
68      // private FilesCache files;
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          // this.parentLayer = parentLayer;
98          this.parentLayer = parentLayer;
99          this.rootName = rootName;
100         this.fileSystemOptions = fileSystemOptions;
101 
102         // this.files = null;
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         // files.put(file.getName(), file);
168     }
169 
170     private FilesCache getCache()
171     {
172         FilesCache files;
173         //if (this.files == null)
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         // return (FileObject) files.get(name);
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         if (root == null)
245         {
246             root = resolveFile(rootName);
247         }
248         return root;
249         */
250     }
251 
252     /***
253      * Finds a file in this file system.
254      */
255     public FileObject resolveFile(final String nameStr) throws FileSystemException
256     {
257         // Resolve the name, and create the file
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         // imario@apache.org ==> use getFileFromCache
280         FileObject file;
281         if (useCache)
282         {
283             file = getFileFromCache(name);
284         }
285         else
286         {
287             file = null;
288         }
289         // FileObject file = (FileObject) files.get(name);
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             // imario@apache.org ==> use putFileToCache
307             if (useCache)
308             {
309                 putFileToCache(file);
310             }
311             // files.put(name, file);
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         // return manager;
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                         // getLogger().warn(message, e);
525                         VfsLog.warn(getLogger(), log, message, e);
526                     }
527                 }
528             }
529         }
530     }
531 
532 	/*
533 	void fileDetached(FileObject fileObject)
534     {
535         useCount--;
536     }
537 
538     void fileAttached(FileObject fileObject)
539     {
540         useCount++;
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 }