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 }