001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.fs;
019    
020    import java.io.Closeable;
021    import java.io.FileNotFoundException;
022    import java.io.IOException;
023    import java.net.URI;
024    import java.security.PrivilegedExceptionAction;
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.EnumSet;
028    import java.util.HashMap;
029    import java.util.HashSet;
030    import java.util.IdentityHashMap;
031    import java.util.Iterator;
032    import java.util.List;
033    import java.util.Map;
034    import java.util.NoSuchElementException;
035    import java.util.Set;
036    import java.util.Stack;
037    import java.util.TreeSet;
038    import java.util.concurrent.atomic.AtomicInteger;
039    import java.util.concurrent.atomic.AtomicLong;
040    
041    import org.apache.commons.logging.Log;
042    import org.apache.commons.logging.LogFactory;
043    import org.apache.hadoop.classification.InterfaceAudience;
044    import org.apache.hadoop.classification.InterfaceStability;
045    import org.apache.hadoop.conf.Configuration;
046    import org.apache.hadoop.conf.Configured;
047    import org.apache.hadoop.fs.Options.Rename;
048    import org.apache.hadoop.fs.permission.FsPermission;
049    import org.apache.hadoop.io.MultipleIOException;
050    import org.apache.hadoop.net.NetUtils;
051    import org.apache.hadoop.security.Credentials;
052    import org.apache.hadoop.security.SecurityUtil;
053    import org.apache.hadoop.security.UserGroupInformation;
054    import org.apache.hadoop.security.token.Token;
055    import org.apache.hadoop.util.Progressable;
056    import org.apache.hadoop.util.ReflectionUtils;
057    
058    /****************************************************************
059     * An abstract base class for a fairly generic filesystem.  It
060     * may be implemented as a distributed filesystem, or as a "local"
061     * one that reflects the locally-connected disk.  The local version
062     * exists for small Hadoop instances and for testing.
063     *
064     * <p>
065     *
066     * All user code that may potentially use the Hadoop Distributed
067     * File System should be written to use a FileSystem object.  The
068     * Hadoop DFS is a multi-machine system that appears as a single
069     * disk.  It's useful because of its fault tolerance and potentially
070     * very large capacity.
071     * 
072     * <p>
073     * The local implementation is {@link LocalFileSystem} and distributed
074     * implementation is DistributedFileSystem.
075     *****************************************************************/
076    @InterfaceAudience.Public
077    @InterfaceStability.Stable
078    public abstract class FileSystem extends Configured implements Closeable {
079      public static final String FS_DEFAULT_NAME_KEY = 
080                       CommonConfigurationKeys.FS_DEFAULT_NAME_KEY;
081      public static final String DEFAULT_FS = 
082                       CommonConfigurationKeys.FS_DEFAULT_NAME_DEFAULT;
083    
084      public static final Log LOG = LogFactory.getLog(FileSystem.class);
085    
086      /** FileSystem cache */
087      static final Cache CACHE = new Cache();
088    
089      /** The key this instance is stored under in the cache. */
090      private Cache.Key key;
091    
092      /** Recording statistics per a FileSystem class */
093      private static final Map<Class<? extends FileSystem>, Statistics> 
094        statisticsTable =
095          new IdentityHashMap<Class<? extends FileSystem>, Statistics>();
096      
097      /**
098       * The statistics for this file system.
099       */
100      protected Statistics statistics;
101    
102      /**
103       * A cache of files that should be deleted when filsystem is closed
104       * or the JVM is exited.
105       */
106      private Set<Path> deleteOnExit = new TreeSet<Path>();
107      
108      /**
109       * This method adds a file system for testing so that we can find it later. It
110       * is only for testing.
111       * @param uri the uri to store it under
112       * @param conf the configuration to store it under
113       * @param fs the file system to store
114       * @throws IOException
115       */
116      static void addFileSystemForTesting(URI uri, Configuration conf,
117          FileSystem fs) throws IOException {
118        CACHE.map.put(new Cache.Key(uri, conf), fs);
119      }
120    
121      /**
122       * Get a filesystem instance based on the uri, the passed
123       * configuration and the user
124       * @param uri of the filesystem
125       * @param conf the configuration to use
126       * @param user to perform the get as
127       * @return the filesystem instance
128       * @throws IOException
129       * @throws InterruptedException
130       */
131      public static FileSystem get(final URI uri, final Configuration conf,
132            final String user) throws IOException, InterruptedException {
133        UserGroupInformation ugi;
134        if (user == null) {
135          ugi = UserGroupInformation.getCurrentUser();
136        } else {
137          ugi = UserGroupInformation.createRemoteUser(user);
138        }
139        return ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
140          public FileSystem run() throws IOException {
141            return get(uri, conf);
142          }
143        });
144      }
145    
146      /**
147       * Returns the configured filesystem implementation.
148       * @param conf the configuration to use
149       */
150      public static FileSystem get(Configuration conf) throws IOException {
151        return get(getDefaultUri(conf), conf);
152      }
153      
154      /** Get the default filesystem URI from a configuration.
155       * @param conf the configuration to use
156       * @return the uri of the default filesystem
157       */
158      public static URI getDefaultUri(Configuration conf) {
159        return URI.create(fixName(conf.get(FS_DEFAULT_NAME_KEY, DEFAULT_FS)));
160      }
161    
162      /** Set the default filesystem URI in a configuration.
163       * @param conf the configuration to alter
164       * @param uri the new default filesystem uri
165       */
166      public static void setDefaultUri(Configuration conf, URI uri) {
167        conf.set(FS_DEFAULT_NAME_KEY, uri.toString());
168      }
169    
170      /** Set the default filesystem URI in a configuration.
171       * @param conf the configuration to alter
172       * @param uri the new default filesystem uri
173       */
174      public static void setDefaultUri(Configuration conf, String uri) {
175        setDefaultUri(conf, URI.create(fixName(uri)));
176      }
177    
178      /** Called after a new FileSystem instance is constructed.
179       * @param name a uri whose authority section names the host, port, etc.
180       *   for this FileSystem
181       * @param conf the configuration
182       */
183      public void initialize(URI name, Configuration conf) throws IOException {
184        statistics = getStatistics(name.getScheme(), getClass());    
185      }
186    
187      /** Returns a URI whose scheme and authority identify this FileSystem.*/
188      public abstract URI getUri();
189      
190      /**
191       * Resolve the uri's hostname and add the default port if not in the uri
192       * @return URI
193       * @see NetUtils#getCanonicalUri(URI, int)
194       */
195      protected URI getCanonicalUri() {
196        return NetUtils.getCanonicalUri(getUri(), getDefaultPort());
197      }
198      
199      /**
200       * Get the default port for this file system.
201       * @return the default port or 0 if there isn't one
202       */
203      protected int getDefaultPort() {
204        return 0;
205      }
206    
207      /**
208       * Get a canonical service name for this file system.  The token cache is
209       * the only user of this value, and uses it to lookup this filesystem's
210       * service tokens.  The token cache will not attempt to acquire tokens if the
211       * service is null.
212       * @return a service string that uniquely identifies this file system, null
213       *         if the filesystem does not implement tokens
214       * @see SecurityUtil#buildDTServiceName(URI, int) 
215       */
216      public String getCanonicalServiceName() {
217        return SecurityUtil.buildDTServiceName(getUri(), getDefaultPort());
218      }
219    
220      /** @deprecated call #getUri() instead.*/
221      @Deprecated
222      public String getName() { return getUri().toString(); }
223    
224      /** @deprecated call #get(URI,Configuration) instead. */
225      @Deprecated
226      public static FileSystem getNamed(String name, Configuration conf)
227        throws IOException {
228        return get(URI.create(fixName(name)), conf);
229      }
230      
231      /** Update old-format filesystem names, for back-compatibility.  This should
232       * eventually be replaced with a checkName() method that throws an exception
233       * for old-format names. */ 
234      private static String fixName(String name) {
235        // convert old-format name to new-format name
236        if (name.equals("local")) {         // "local" is now "file:///".
237          LOG.warn("\"local\" is a deprecated filesystem name."
238                   +" Use \"file:///\" instead.");
239          name = "file:///";
240        } else if (name.indexOf('/')==-1) {   // unqualified is "hdfs://"
241          LOG.warn("\""+name+"\" is a deprecated filesystem name."
242                   +" Use \"hdfs://"+name+"/\" instead.");
243          name = "hdfs://"+name;
244        }
245        return name;
246      }
247    
248      /**
249       * Get the local file system.
250       * @param conf the configuration to configure the file system with
251       * @return a LocalFileSystem
252       */
253      public static LocalFileSystem getLocal(Configuration conf)
254        throws IOException {
255        return (LocalFileSystem)get(LocalFileSystem.NAME, conf);
256      }
257    
258      /** Returns the FileSystem for this URI's scheme and authority.  The scheme
259       * of the URI determines a configuration property name,
260       * <tt>fs.<i>scheme</i>.class</tt> whose value names the FileSystem class.
261       * The entire URI is passed to the FileSystem instance's initialize method.
262       */
263      public static FileSystem get(URI uri, Configuration conf) throws IOException {
264        String scheme = uri.getScheme();
265        String authority = uri.getAuthority();
266    
267        if (scheme == null) {                       // no scheme: use default FS
268          return get(conf);
269        }
270    
271        if (authority == null) {                       // no authority
272          URI defaultUri = getDefaultUri(conf);
273          if (scheme.equals(defaultUri.getScheme())    // if scheme matches default
274              && defaultUri.getAuthority() != null) {  // & default has authority
275            return get(defaultUri, conf);              // return default
276          }
277        }
278        
279        String disableCacheName = String.format("fs.%s.impl.disable.cache", scheme);
280        if (conf.getBoolean(disableCacheName, false)) {
281          return createFileSystem(uri, conf);
282        }
283    
284        return CACHE.get(uri, conf);
285      }
286    
287      /**
288       * Returns the FileSystem for this URI's scheme and authority and the 
289       * passed user. Internally invokes {@link #newInstance(URI, Configuration)}
290       * @param uri of the filesystem
291       * @param conf the configuration to use
292       * @param user to perform the get as
293       * @return filesystem instance
294       * @throws IOException
295       * @throws InterruptedException
296       */
297      public static FileSystem newInstance(final URI uri, final Configuration conf,
298          final String user) throws IOException, InterruptedException {
299        UserGroupInformation ugi;
300        if (user == null) {
301          ugi = UserGroupInformation.getCurrentUser();
302        } else {
303          ugi = UserGroupInformation.createRemoteUser(user);
304        }
305        return ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
306          public FileSystem run() throws IOException {
307            return newInstance(uri,conf); 
308          }
309        });
310      }
311      /** Returns the FileSystem for this URI's scheme and authority.  The scheme
312       * of the URI determines a configuration property name,
313       * <tt>fs.<i>scheme</i>.class</tt> whose value names the FileSystem class.
314       * The entire URI is passed to the FileSystem instance's initialize method.
315       * This always returns a new FileSystem object.
316       */
317      public static FileSystem newInstance(URI uri, Configuration conf) throws IOException {
318        String scheme = uri.getScheme();
319        String authority = uri.getAuthority();
320    
321        if (scheme == null) {                       // no scheme: use default FS
322          return newInstance(conf);
323        }
324    
325        if (authority == null) {                       // no authority
326          URI defaultUri = getDefaultUri(conf);
327          if (scheme.equals(defaultUri.getScheme())    // if scheme matches default
328              && defaultUri.getAuthority() != null) {  // & default has authority
329            return newInstance(defaultUri, conf);              // return default
330          }
331        }
332        return CACHE.getUnique(uri, conf);
333      }
334    
335      /** Returns a unique configured filesystem implementation.
336       * This always returns a new FileSystem object.
337       * @param conf the configuration to use
338       */
339      public static FileSystem newInstance(Configuration conf) throws IOException {
340        return newInstance(getDefaultUri(conf), conf);
341      }
342    
343      /**
344       * Get a unique local file system object
345       * @param conf the configuration to configure the file system with
346       * @return a LocalFileSystem
347       * This always returns a new FileSystem object.
348       */
349      public static LocalFileSystem newInstanceLocal(Configuration conf)
350        throws IOException {
351        return (LocalFileSystem)newInstance(LocalFileSystem.NAME, conf);
352      }
353    
354      /**
355       * Close all cached filesystems. Be sure those filesystems are not
356       * used anymore.
357       * 
358       * @throws IOException
359       */
360      public static void closeAll() throws IOException {
361        CACHE.closeAll();
362      }
363    
364      /**
365       * Close all cached filesystems for a given UGI. Be sure those filesystems 
366       * are not used anymore.
367       * @param ugi user group info to close
368       * @throws IOException
369       */
370      public static void closeAllForUGI(UserGroupInformation ugi) 
371      throws IOException {
372        CACHE.closeAll(ugi);
373      }
374    
375      /** 
376       * Make sure that a path specifies a FileSystem.
377       * @param path to use
378       */
379      public Path makeQualified(Path path) {
380        checkPath(path);
381        return path.makeQualified(this.getUri(), this.getWorkingDirectory());
382      }
383        
384      /**
385       * Deprecated  - use @link {@link #getDelegationTokens(String)}
386       * Get a new delegation token for this file system.
387       * @param renewer the account name that is allowed to renew the token.
388       * @return a new delegation token
389       * @throws IOException
390       */
391      @InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
392      @Deprecated
393      public Token<?> getDelegationToken(String renewer) throws IOException {
394        return null;
395      }
396      
397      /**
398       * Get one or more delegation tokens associated with the filesystem. Normally
399       * a file system returns a single delegation token. A file system that manages
400       * multiple file systems underneath, could return set of delegation tokens for
401       * all the file systems it manages.
402       * 
403       * @param renewer the account name that is allowed to renew the token.
404       * @return list of new delegation tokens
405       *    If delegation tokens not supported then return a list of size zero.
406       * @throws IOException
407       */
408      @InterfaceAudience.LimitedPrivate( { "HDFS", "MapReduce" })
409      public List<Token<?>> getDelegationTokens(String renewer) throws IOException {
410        return new ArrayList<Token<?>>(0);
411      }
412      
413      /**
414       * @see #getDelegationTokens(String)
415       * This is similar to getDelegationTokens, with the added restriction that if
416       * a token is already present in the passed Credentials object - that token
417       * is returned instead of a new delegation token. 
418       * 
419       * If the token is found to be cached in the Credentials object, this API does
420       * not verify the token validity or the passed in renewer. 
421       * 
422       * 
423       * @param renewer the account name that is allowed to renew the token.
424       * @param credentials a Credentials object containing already knowing 
425       *   delegationTokens.
426       * @return a list of delegation tokens.
427       * @throws IOException
428       */
429      @InterfaceAudience.LimitedPrivate({ "HDFS", "MapReduce" })
430      public List<Token<?>> getDelegationTokens(String renewer,
431          Credentials credentials) throws IOException {
432        List<Token<?>> allTokens = getDelegationTokens(renewer);
433        List<Token<?>> newTokens = new ArrayList<Token<?>>();
434        if (allTokens != null) {
435          for (Token<?> token : allTokens) {
436            Token<?> knownToken = credentials.getToken(token.getService());
437            if (knownToken == null) {
438              newTokens.add(token);
439            } else {
440              newTokens.add(knownToken);
441            }
442          }
443        }
444        return newTokens;
445      }
446    
447      /** create a file with the provided permission
448       * The permission of the file is set to be the provided permission as in
449       * setPermission, not permission&~umask
450       * 
451       * It is implemented using two RPCs. It is understood that it is inefficient,
452       * but the implementation is thread-safe. The other option is to change the
453       * value of umask in configuration to be 0, but it is not thread-safe.
454       * 
455       * @param fs file system handle
456       * @param file the name of the file to be created
457       * @param permission the permission of the file
458       * @return an output stream
459       * @throws IOException
460       */
461      public static FSDataOutputStream create(FileSystem fs,
462          Path file, FsPermission permission) throws IOException {
463        // create the file with default permission
464        FSDataOutputStream out = fs.create(file);
465        // set its permission to the supplied one
466        fs.setPermission(file, permission);
467        return out;
468      }
469    
470      /** create a directory with the provided permission
471       * The permission of the directory is set to be the provided permission as in
472       * setPermission, not permission&~umask
473       * 
474       * @see #create(FileSystem, Path, FsPermission)
475       * 
476       * @param fs file system handle
477       * @param dir the name of the directory to be created
478       * @param permission the permission of the directory
479       * @return true if the directory creation succeeds; false otherwise
480       * @throws IOException
481       */
482      public static boolean mkdirs(FileSystem fs, Path dir, FsPermission permission)
483      throws IOException {
484        // create the directory using the default permission
485        boolean result = fs.mkdirs(dir);
486        // set its permission to be the supplied one
487        fs.setPermission(dir, permission);
488        return result;
489      }
490    
491      ///////////////////////////////////////////////////////////////
492      // FileSystem
493      ///////////////////////////////////////////////////////////////
494    
495      protected FileSystem() {
496        super(null);
497      }
498    
499      /** 
500       * Check that a Path belongs to this FileSystem.
501       * @param path to check
502       */
503      protected void checkPath(Path path) {
504        URI uri = path.toUri();
505        String thatScheme = uri.getScheme();
506        if (thatScheme == null)                // fs is relative
507          return;
508        URI thisUri = getCanonicalUri();
509        String thisScheme = thisUri.getScheme();
510        //authority and scheme are not case sensitive
511        if (thisScheme.equalsIgnoreCase(thatScheme)) {// schemes match
512          String thisAuthority = thisUri.getAuthority();
513          String thatAuthority = uri.getAuthority();
514          if (thatAuthority == null &&                // path's authority is null
515              thisAuthority != null) {                // fs has an authority
516            URI defaultUri = getDefaultUri(getConf());
517            if (thisScheme.equalsIgnoreCase(defaultUri.getScheme())) {
518              uri = defaultUri; // schemes match, so use this uri instead
519            } else {
520              uri = null; // can't determine auth of the path
521            }
522          }
523          if (uri != null) {
524            // canonicalize uri before comparing with this fs
525            uri = NetUtils.getCanonicalUri(uri, getDefaultPort());
526            thatAuthority = uri.getAuthority();
527            if (thisAuthority == thatAuthority ||       // authorities match
528                (thisAuthority != null &&
529                 thisAuthority.equalsIgnoreCase(thatAuthority)))
530              return;
531          }
532        }
533        throw new IllegalArgumentException("Wrong FS: "+path+
534                                           ", expected: "+this.getUri());
535      }
536    
537      /**
538       * Return an array containing hostnames, offset and size of 
539       * portions of the given file.  For a nonexistent 
540       * file or regions, null will be returned.
541       *
542       * This call is most helpful with DFS, where it returns 
543       * hostnames of machines that contain the given file.
544       *
545       * The FileSystem will simply return an elt containing 'localhost'.
546       *
547       * @param file FilesStatus to get data from
548       * @param start offset into the given file
549       * @param len length for which to get locations for
550       */
551      public BlockLocation[] getFileBlockLocations(FileStatus file, 
552          long start, long len) throws IOException {
553        if (file == null) {
554          return null;
555        }
556    
557        if (start < 0 || len < 0) {
558          throw new IllegalArgumentException("Invalid start or len parameter");
559        }
560    
561        if (file.getLen() < start) {
562          return new BlockLocation[0];
563    
564        }
565        String[] name = { "localhost:50010" };
566        String[] host = { "localhost" };
567        return new BlockLocation[] {
568          new BlockLocation(name, host, 0, file.getLen()) };
569      }
570     
571    
572      /**
573       * Return an array containing hostnames, offset and size of 
574       * portions of the given file.  For a nonexistent 
575       * file or regions, null will be returned.
576       *
577       * This call is most helpful with DFS, where it returns 
578       * hostnames of machines that contain the given file.
579       *
580       * The FileSystem will simply return an elt containing 'localhost'.
581       *
582       * @param p path of file to get locations for
583       * @param start offset into the given file
584       * @param len length for which to get locations for
585       */
586      public BlockLocation[] getFileBlockLocations(Path p, 
587          long start, long len) throws IOException {
588        if (p == null) {
589          throw new NullPointerException();
590        }
591        FileStatus file = getFileStatus(p);
592        return getFileBlockLocations(file, start, len);
593      }
594      
595      /**
596       * Return a set of server default configuration values
597       * @return server default configuration values
598       * @throws IOException
599       */
600      public FsServerDefaults getServerDefaults() throws IOException {
601        Configuration conf = getConf();
602        return new FsServerDefaults(getDefaultBlockSize(), 
603            conf.getInt("io.bytes.per.checksum", 512), 
604            64 * 1024, 
605            getDefaultReplication(), 
606            conf.getInt("io.file.buffer.size", 4096));
607      }
608      
609      /**
610       * Return the fully-qualified path of path f resolving the path
611       * through any symlinks or mount point
612       * @param p path to be resolved
613       * @return fully qualified path 
614       * @throws FileNotFoundException
615       */
616       public Path resolvePath(final Path p) throws IOException {
617         checkPath(p);
618         return getFileStatus(p).getPath();
619       }
620    
621      /**
622       * Opens an FSDataInputStream at the indicated Path.
623       * @param f the file name to open
624       * @param bufferSize the size of the buffer to be used.
625       */
626      public abstract FSDataInputStream open(Path f, int bufferSize)
627        throws IOException;
628        
629      /**
630       * Opens an FSDataInputStream at the indicated Path.
631       * @param f the file to open
632       */
633      public FSDataInputStream open(Path f) throws IOException {
634        return open(f, getConf().getInt("io.file.buffer.size", 4096));
635      }
636    
637      /**
638       * Create an FSDataOutputStream at the indicated Path.
639       * Files are overwritten by default.
640       * @param f the file to create
641       */
642      public FSDataOutputStream create(Path f) throws IOException {
643        return create(f, true);
644      }
645    
646      /**
647       * Create an FSDataOutputStream at the indicated Path.
648       * @param f the file to create
649       * @param overwrite if a file with this name already exists, then if true,
650       *   the file will be overwritten, and if false an exception will be thrown.
651       */
652      public FSDataOutputStream create(Path f, boolean overwrite)
653          throws IOException {
654        return create(f, overwrite, 
655                      getConf().getInt("io.file.buffer.size", 4096),
656                      getDefaultReplication(),
657                      getDefaultBlockSize());
658      }
659    
660      /**
661       * Create an FSDataOutputStream at the indicated Path with write-progress
662       * reporting.
663       * Files are overwritten by default.
664       * @param f the file to create
665       * @param progress to report progress
666       */
667      public FSDataOutputStream create(Path f, Progressable progress) 
668          throws IOException {
669        return create(f, true, 
670                      getConf().getInt("io.file.buffer.size", 4096),
671                      getDefaultReplication(),
672                      getDefaultBlockSize(), progress);
673      }
674    
675      /**
676       * Create an FSDataOutputStream at the indicated Path.
677       * Files are overwritten by default.
678       * @param f the file to create
679       * @param replication the replication factor
680       */
681      public FSDataOutputStream create(Path f, short replication)
682          throws IOException {
683        return create(f, true, 
684                      getConf().getInt("io.file.buffer.size", 4096),
685                      replication,
686                      getDefaultBlockSize());
687      }
688    
689      /**
690       * Create an FSDataOutputStream at the indicated Path with write-progress
691       * reporting.
692       * Files are overwritten by default.
693       * @param f the file to create
694       * @param replication the replication factor
695       * @param progress to report progress
696       */
697      public FSDataOutputStream create(Path f, short replication, 
698          Progressable progress) throws IOException {
699        return create(f, true, 
700                      getConf().getInt("io.file.buffer.size", 4096),
701                      replication,
702                      getDefaultBlockSize(), progress);
703      }
704    
705        
706      /**
707       * Create an FSDataOutputStream at the indicated Path.
708       * @param f the file name to create
709       * @param overwrite if a file with this name already exists, then if true,
710       *   the file will be overwritten, and if false an error will be thrown.
711       * @param bufferSize the size of the buffer to be used.
712       */
713      public FSDataOutputStream create(Path f, 
714                                       boolean overwrite,
715                                       int bufferSize
716                                       ) throws IOException {
717        return create(f, overwrite, bufferSize, 
718                      getDefaultReplication(),
719                      getDefaultBlockSize());
720      }
721        
722      /**
723       * Create an FSDataOutputStream at the indicated Path with write-progress
724       * reporting.
725       * @param f the path of the file to open
726       * @param overwrite if a file with this name already exists, then if true,
727       *   the file will be overwritten, and if false an error will be thrown.
728       * @param bufferSize the size of the buffer to be used.
729       */
730      public FSDataOutputStream create(Path f, 
731                                       boolean overwrite,
732                                       int bufferSize,
733                                       Progressable progress
734                                       ) throws IOException {
735        return create(f, overwrite, bufferSize, 
736                      getDefaultReplication(),
737                      getDefaultBlockSize(), progress);
738      }
739        
740        
741      /**
742       * Create an FSDataOutputStream at the indicated Path.
743       * @param f the file name to open
744       * @param overwrite if a file with this name already exists, then if true,
745       *   the file will be overwritten, and if false an error will be thrown.
746       * @param bufferSize the size of the buffer to be used.
747       * @param replication required block replication for the file. 
748       */
749      public FSDataOutputStream create(Path f, 
750                                       boolean overwrite,
751                                       int bufferSize,
752                                       short replication,
753                                       long blockSize
754                                       ) throws IOException {
755        return create(f, overwrite, bufferSize, replication, blockSize, null);
756      }
757    
758      /**
759       * Create an FSDataOutputStream at the indicated Path with write-progress
760       * reporting.
761       * @param f the file name to open
762       * @param overwrite if a file with this name already exists, then if true,
763       *   the file will be overwritten, and if false an error will be thrown.
764       * @param bufferSize the size of the buffer to be used.
765       * @param replication required block replication for the file. 
766       */
767      public FSDataOutputStream create(Path f,
768                                                boolean overwrite,
769                                                int bufferSize,
770                                                short replication,
771                                                long blockSize,
772                                                Progressable progress
773                                                ) throws IOException {
774        return this.create(f, FsPermission.getDefault().applyUMask(
775            FsPermission.getUMask(getConf())), overwrite, bufferSize,
776            replication, blockSize, progress);
777      }
778    
779      /**
780       * Create an FSDataOutputStream at the indicated Path with write-progress
781       * reporting.
782       * @param f the file name to open
783       * @param permission
784       * @param overwrite if a file with this name already exists, then if true,
785       *   the file will be overwritten, and if false an error will be thrown.
786       * @param bufferSize the size of the buffer to be used.
787       * @param replication required block replication for the file.
788       * @param blockSize
789       * @param progress
790       * @throws IOException
791       * @see #setPermission(Path, FsPermission)
792       */
793      public abstract FSDataOutputStream create(Path f,
794          FsPermission permission,
795          boolean overwrite,
796          int bufferSize,
797          short replication,
798          long blockSize,
799          Progressable progress) throws IOException;
800      
801      
802      /*.
803       * This create has been added to support the FileContext that processes
804       * the permission
805       * with umask before calling this method.
806       * This a temporary method added to support the transition from FileSystem
807       * to FileContext for user applications.
808       */
809      @Deprecated
810      protected FSDataOutputStream primitiveCreate(Path f,
811         FsPermission absolutePermission, EnumSet<CreateFlag> flag, int bufferSize,
812         short replication, long blockSize, Progressable progress,
813         int bytesPerChecksum) throws IOException {
814    
815        boolean pathExists = exists(f);
816        CreateFlag.validate(f, pathExists, flag);
817        
818        // Default impl  assumes that permissions do not matter and 
819        // nor does the bytesPerChecksum  hence
820        // calling the regular create is good enough.
821        // FSs that implement permissions should override this.
822    
823        if (pathExists && flag.contains(CreateFlag.APPEND)) {
824          return append(f, bufferSize, progress);
825        }
826        
827        return this.create(f, absolutePermission,
828            flag.contains(CreateFlag.OVERWRITE), bufferSize, replication,
829            blockSize, progress);
830      }
831      
832      /**
833       * This version of the mkdirs method assumes that the permission is absolute.
834       * It has been added to support the FileContext that processes the permission
835       * with umask before calling this method.
836       * This a temporary method added to support the transition from FileSystem
837       * to FileContext for user applications.
838       */
839      @Deprecated
840      protected boolean primitiveMkdir(Path f, FsPermission absolutePermission)
841        throws IOException {
842        // Default impl is to assume that permissions do not matter and hence
843        // calling the regular mkdirs is good enough.
844        // FSs that implement permissions should override this.
845       return this.mkdirs(f, absolutePermission);
846      }
847    
848    
849      /**
850       * This version of the mkdirs method assumes that the permission is absolute.
851       * It has been added to support the FileContext that processes the permission
852       * with umask before calling this method.
853       * This a temporary method added to support the transition from FileSystem
854       * to FileContext for user applications.
855       */
856      @Deprecated
857      protected void primitiveMkdir(Path f, FsPermission absolutePermission, 
858                        boolean createParent)
859        throws IOException {
860        
861        if (!createParent) { // parent must exist.
862          // since the this.mkdirs makes parent dirs automatically
863          // we must throw exception if parent does not exist.
864          final FileStatus stat = getFileStatus(f.getParent());
865          if (stat == null) {
866            throw new FileNotFoundException("Missing parent:" + f);
867          }
868          if (!stat.isDirectory()) {
869            throw new ParentNotDirectoryException("parent is not a dir");
870          }
871          // parent does exist - go ahead with mkdir of leaf
872        }
873        // Default impl is to assume that permissions do not matter and hence
874        // calling the regular mkdirs is good enough.
875        // FSs that implement permissions should override this.
876        if (!this.mkdirs(f, absolutePermission)) {
877          throw new IOException("mkdir of "+ f + " failed");
878        }
879      }
880    
881      /**
882       * Opens an FSDataOutputStream at the indicated Path with write-progress
883       * reporting. Same as create(), except fails if parent directory doesn't
884       * already exist.
885       * @param f the file name to open
886       * @param overwrite if a file with this name already exists, then if true,
887       * the file will be overwritten, and if false an error will be thrown.
888       * @param bufferSize the size of the buffer to be used.
889       * @param replication required block replication for the file.
890       * @param blockSize
891       * @param progress
892       * @throws IOException
893       * @see #setPermission(Path, FsPermission)
894       * @deprecated API only for 0.20-append
895       */
896      @Deprecated
897      public FSDataOutputStream createNonRecursive(Path f,
898          boolean overwrite,
899          int bufferSize, short replication, long blockSize,
900          Progressable progress) throws IOException {
901        return this.createNonRecursive(f, FsPermission.getDefault(),
902            overwrite, bufferSize, replication, blockSize, progress);
903      }
904    
905      /**
906       * Opens an FSDataOutputStream at the indicated Path with write-progress
907       * reporting. Same as create(), except fails if parent directory doesn't
908       * already exist.
909       * @param f the file name to open
910       * @param permission
911       * @param overwrite if a file with this name already exists, then if true,
912       * the file will be overwritten, and if false an error will be thrown.
913       * @param bufferSize the size of the buffer to be used.
914       * @param replication required block replication for the file.
915       * @param blockSize
916       * @param progress
917       * @throws IOException
918       * @see #setPermission(Path, FsPermission)
919       * @deprecated API only for 0.20-append
920       */
921       @Deprecated
922       public FSDataOutputStream createNonRecursive(Path f, FsPermission permission,
923           boolean overwrite, int bufferSize, short replication, long blockSize,
924           Progressable progress) throws IOException {
925         throw new IOException("createNonRecursive unsupported for this filesystem "
926             + this.getClass());
927       }
928    
929      /**
930       * Creates the given Path as a brand-new zero-length file.  If
931       * create fails, or if it already existed, return false.
932       *
933       * @param f path to use for create
934       */
935      public boolean createNewFile(Path f) throws IOException {
936        if (exists(f)) {
937          return false;
938        } else {
939          create(f, false, getConf().getInt("io.file.buffer.size", 4096)).close();
940          return true;
941        }
942      }
943    
944      /**
945       * Append to an existing file (optional operation).
946       * Same as append(f, getConf().getInt("io.file.buffer.size", 4096), null)
947       * @param f the existing file to be appended.
948       * @throws IOException
949       */
950      public FSDataOutputStream append(Path f) throws IOException {
951        return append(f, getConf().getInt("io.file.buffer.size", 4096), null);
952      }
953      /**
954       * Append to an existing file (optional operation).
955       * Same as append(f, bufferSize, null).
956       * @param f the existing file to be appended.
957       * @param bufferSize the size of the buffer to be used.
958       * @throws IOException
959       */
960      public FSDataOutputStream append(Path f, int bufferSize) throws IOException {
961        return append(f, bufferSize, null);
962      }
963    
964      /**
965       * Append to an existing file (optional operation).
966       * @param f the existing file to be appended.
967       * @param bufferSize the size of the buffer to be used.
968       * @param progress for reporting progress if it is not null.
969       * @throws IOException
970       */
971      public abstract FSDataOutputStream append(Path f, int bufferSize,
972          Progressable progress) throws IOException;
973    
974     /**
975       * Get replication.
976       * 
977       * @deprecated Use getFileStatus() instead
978       * @param src file name
979       * @return file replication
980       * @throws IOException
981       */ 
982      @Deprecated
983      public short getReplication(Path src) throws IOException {
984        return getFileStatus(src).getReplication();
985      }
986    
987      /**
988       * Set replication for an existing file.
989       * 
990       * @param src file name
991       * @param replication new replication
992       * @throws IOException
993       * @return true if successful;
994       *         false if file does not exist or is a directory
995       */
996      public boolean setReplication(Path src, short replication)
997        throws IOException {
998        return true;
999      }
1000    
1001      /**
1002       * Renames Path src to Path dst.  Can take place on local fs
1003       * or remote DFS.
1004       * @param src path to be renamed
1005       * @param dst new path after rename
1006       * @throws IOException on failure
1007       * @return true if rename is successful
1008       */
1009      public abstract boolean rename(Path src, Path dst) throws IOException;
1010    
1011      /**
1012       * Renames Path src to Path dst
1013       * <ul>
1014       * <li
1015       * <li>Fails if src is a file and dst is a directory.
1016       * <li>Fails if src is a directory and dst is a file.
1017       * <li>Fails if the parent of dst does not exist or is a file.
1018       * </ul>
1019       * <p>
1020       * If OVERWRITE option is not passed as an argument, rename fails
1021       * if the dst already exists.
1022       * <p>
1023       * If OVERWRITE option is passed as an argument, rename overwrites
1024       * the dst if it is a file or an empty directory. Rename fails if dst is
1025       * a non-empty directory.
1026       * <p>
1027       * Note that atomicity of rename is dependent on the file system
1028       * implementation. Please refer to the file system documentation for
1029       * details. This default implementation is non atomic.
1030       * <p>
1031       * This method is deprecated since it is a temporary method added to 
1032       * support the transition from FileSystem to FileContext for user 
1033       * applications.
1034       * 
1035       * @param src path to be renamed
1036       * @param dst new path after rename
1037       * @throws IOException on failure
1038       */
1039      @Deprecated
1040      protected void rename(final Path src, final Path dst,
1041          final Rename... options) throws IOException {
1042        // Default implementation
1043        final FileStatus srcStatus = getFileStatus(src);
1044        if (srcStatus == null) {
1045          throw new FileNotFoundException("rename source " + src + " not found.");
1046        }
1047    
1048        boolean overwrite = false;
1049        if (null != options) {
1050          for (Rename option : options) {
1051            if (option == Rename.OVERWRITE) {
1052              overwrite = true;
1053            }
1054          }
1055        }
1056    
1057        FileStatus dstStatus;
1058        try {
1059          dstStatus = getFileStatus(dst);
1060        } catch (IOException e) {
1061          dstStatus = null;
1062        }
1063        if (dstStatus != null) {
1064          if (srcStatus.isDirectory() != dstStatus.isDirectory()) {
1065            throw new IOException("Source " + src + " Destination " + dst
1066                + " both should be either file or directory");
1067          }
1068          if (!overwrite) {
1069            throw new FileAlreadyExistsException("rename destination " + dst
1070                + " already exists.");
1071          }
1072          // Delete the destination that is a file or an empty directory
1073          if (dstStatus.isDirectory()) {
1074            FileStatus[] list = listStatus(dst);
1075            if (list != null && list.length != 0) {
1076              throw new IOException(
1077                  "rename cannot overwrite non empty destination directory " + dst);
1078            }
1079          }
1080          delete(dst, false);
1081        } else {
1082          final Path parent = dst.getParent();
1083          final FileStatus parentStatus = getFileStatus(parent);
1084          if (parentStatus == null) {
1085            throw new FileNotFoundException("rename destination parent " + parent
1086                + " not found.");
1087          }
1088          if (!parentStatus.isDirectory()) {
1089            throw new ParentNotDirectoryException("rename destination parent " + parent
1090                + " is a file.");
1091          }
1092        }
1093        if (!rename(src, dst)) {
1094          throw new IOException("rename from " + src + " to " + dst + " failed.");
1095        }
1096      }
1097      
1098      /**
1099       * Delete a file 
1100       * @deprecated Use {@link #delete(Path, boolean)} instead.
1101       */
1102      @Deprecated
1103      public boolean delete(Path f) throws IOException {
1104        return delete(f, true);
1105      }
1106      
1107      /** Delete a file.
1108       *
1109       * @param f the path to delete.
1110       * @param recursive if path is a directory and set to 
1111       * true, the directory is deleted else throws an exception. In
1112       * case of a file the recursive can be set to either true or false. 
1113       * @return  true if delete is successful else false. 
1114       * @throws IOException
1115       */
1116      public abstract boolean delete(Path f, boolean recursive) throws IOException;
1117    
1118      /**
1119       * Mark a path to be deleted when FileSystem is closed.
1120       * When the JVM shuts down,
1121       * all FileSystem objects will be closed automatically.
1122       * Then,
1123       * the marked path will be deleted as a result of closing the FileSystem.
1124       *
1125       * The path has to exist in the file system.
1126       * 
1127       * @param f the path to delete.
1128       * @return  true if deleteOnExit is successful, otherwise false.
1129       * @throws IOException
1130       */
1131      public boolean deleteOnExit(Path f) throws IOException {
1132        if (!exists(f)) {
1133          return false;
1134        }
1135        synchronized (deleteOnExit) {
1136          deleteOnExit.add(f);
1137        }
1138        return true;
1139      }
1140    
1141      /**
1142       * Delete all files that were marked as delete-on-exit. This recursively
1143       * deletes all files in the specified paths.
1144       */
1145      protected void processDeleteOnExit() {
1146        synchronized (deleteOnExit) {
1147          for (Iterator<Path> iter = deleteOnExit.iterator(); iter.hasNext();) {
1148            Path path = iter.next();
1149            try {
1150              delete(path, true);
1151            }
1152            catch (IOException e) {
1153              LOG.info("Ignoring failure to deleteOnExit for path " + path);
1154            }
1155            iter.remove();
1156          }
1157        }
1158      }
1159      
1160      /** Check if exists.
1161       * @param f source file
1162       */
1163      public boolean exists(Path f) throws IOException {
1164        try {
1165          return getFileStatus(f) != null;
1166        } catch (FileNotFoundException e) {
1167          return false;
1168        }
1169      }
1170    
1171      /** True iff the named path is a directory.
1172       * Note: Avoid using this method. Instead reuse the FileStatus 
1173       * returned by getFileStatus() or listStatus() methods.
1174       * @param f path to check
1175       */
1176      public boolean isDirectory(Path f) throws IOException {
1177        try {
1178          return getFileStatus(f).isDirectory();
1179        } catch (FileNotFoundException e) {
1180          return false;               // f does not exist
1181        }
1182      }
1183    
1184      /** True iff the named path is a regular file.
1185       * Note: Avoid using this method. Instead reuse the FileStatus 
1186       * returned by getFileStatus() or listStatus() methods.
1187       * @param f path to check
1188       */
1189      public boolean isFile(Path f) throws IOException {
1190        try {
1191          return getFileStatus(f).isFile();
1192        } catch (FileNotFoundException e) {
1193          return false;               // f does not exist
1194        }
1195      }
1196      
1197      /** The number of bytes in a file. */
1198      /** @deprecated Use getFileStatus() instead */
1199      @Deprecated
1200      public long getLength(Path f) throws IOException {
1201        return getFileStatus(f).getLen();
1202      }
1203        
1204      /** Return the {@link ContentSummary} of a given {@link Path}.
1205      * @param f path to use
1206      */
1207      public ContentSummary getContentSummary(Path f) throws IOException {
1208        FileStatus status = getFileStatus(f);
1209        if (status.isFile()) {
1210          // f is a file
1211          return new ContentSummary(status.getLen(), 1, 0);
1212        }
1213        // f is a directory
1214        long[] summary = {0, 0, 1};
1215        for(FileStatus s : listStatus(f)) {
1216          ContentSummary c = s.isDirectory() ? getContentSummary(s.getPath()) :
1217                                         new ContentSummary(s.getLen(), 1, 0);
1218          summary[0] += c.getLength();
1219          summary[1] += c.getFileCount();
1220          summary[2] += c.getDirectoryCount();
1221        }
1222        return new ContentSummary(summary[0], summary[1], summary[2]);
1223      }
1224    
1225      final private static PathFilter DEFAULT_FILTER = new PathFilter() {
1226          public boolean accept(Path file) {
1227            return true;
1228          }     
1229        };
1230        
1231      /**
1232       * List the statuses of the files/directories in the given path if the path is
1233       * a directory.
1234       * 
1235       * @param f given path
1236       * @return the statuses of the files/directories in the given patch
1237       * @throws FileNotFoundException when the path does not exist;
1238       *         IOException see specific implementation
1239       */
1240      public abstract FileStatus[] listStatus(Path f) throws FileNotFoundException, 
1241                                                             IOException;
1242        
1243      /*
1244       * Filter files/directories in the given path using the user-supplied path
1245       * filter. Results are added to the given array <code>results</code>.
1246       */
1247      private void listStatus(ArrayList<FileStatus> results, Path f,
1248          PathFilter filter) throws FileNotFoundException, IOException {
1249        FileStatus listing[] = listStatus(f);
1250        if (listing == null) {
1251          throw new IOException("Error accessing " + f);
1252        }
1253    
1254        for (int i = 0; i < listing.length; i++) {
1255          if (filter.accept(listing[i].getPath())) {
1256            results.add(listing[i]);
1257          }
1258        }
1259      }
1260    
1261      /**
1262       * @return an iterator over the corrupt files under the given path
1263       * (may contain duplicates if a file has more than one corrupt block)
1264       * @throws IOException
1265       */
1266      public RemoteIterator<Path> listCorruptFileBlocks(Path path)
1267        throws IOException {
1268        throw new UnsupportedOperationException(getClass().getCanonicalName() +
1269                                                " does not support" +
1270                                                " listCorruptFileBlocks");
1271      }
1272    
1273      /**
1274       * Filter files/directories in the given path using the user-supplied path
1275       * filter.
1276       * 
1277       * @param f
1278       *          a path name
1279       * @param filter
1280       *          the user-supplied path filter
1281       * @return an array of FileStatus objects for the files under the given path
1282       *         after applying the filter
1283       * @throws FileNotFoundException when the path does not exist;
1284       *         IOException see specific implementation   
1285       */
1286      public FileStatus[] listStatus(Path f, PathFilter filter) 
1287                                       throws FileNotFoundException, IOException {
1288        ArrayList<FileStatus> results = new ArrayList<FileStatus>();
1289        listStatus(results, f, filter);
1290        return results.toArray(new FileStatus[results.size()]);
1291      }
1292    
1293      /**
1294       * Filter files/directories in the given list of paths using default
1295       * path filter.
1296       * 
1297       * @param files
1298       *          a list of paths
1299       * @return a list of statuses for the files under the given paths after
1300       *         applying the filter default Path filter
1301       * @throws FileNotFoundException when the path does not exist;
1302       *         IOException see specific implementation
1303       */
1304      public FileStatus[] listStatus(Path[] files)
1305          throws FileNotFoundException, IOException {
1306        return listStatus(files, DEFAULT_FILTER);
1307      }
1308    
1309      /**
1310       * Filter files/directories in the given list of paths using user-supplied
1311       * path filter.
1312       * 
1313       * @param files
1314       *          a list of paths
1315       * @param filter
1316       *          the user-supplied path filter
1317       * @return a list of statuses for the files under the given paths after
1318       *         applying the filter
1319       * @throws FileNotFoundException when the path does not exist;
1320       *         IOException see specific implementation
1321       */
1322      public FileStatus[] listStatus(Path[] files, PathFilter filter)
1323          throws FileNotFoundException, IOException {
1324        ArrayList<FileStatus> results = new ArrayList<FileStatus>();
1325        for (int i = 0; i < files.length; i++) {
1326          listStatus(results, files[i], filter);
1327        }
1328        return results.toArray(new FileStatus[results.size()]);
1329      }
1330    
1331      /**
1332       * <p>Return all the files that match filePattern and are not checksum
1333       * files. Results are sorted by their names.
1334       * 
1335       * <p>
1336       * A filename pattern is composed of <i>regular</i> characters and
1337       * <i>special pattern matching</i> characters, which are:
1338       *
1339       * <dl>
1340       *  <dd>
1341       *   <dl>
1342       *    <p>
1343       *    <dt> <tt> ? </tt>
1344       *    <dd> Matches any single character.
1345       *
1346       *    <p>
1347       *    <dt> <tt> * </tt>
1348       *    <dd> Matches zero or more characters.
1349       *
1350       *    <p>
1351       *    <dt> <tt> [<i>abc</i>] </tt>
1352       *    <dd> Matches a single character from character set
1353       *     <tt>{<i>a,b,c</i>}</tt>.
1354       *
1355       *    <p>
1356       *    <dt> <tt> [<i>a</i>-<i>b</i>] </tt>
1357       *    <dd> Matches a single character from the character range
1358       *     <tt>{<i>a...b</i>}</tt>.  Note that character <tt><i>a</i></tt> must be
1359       *     lexicographically less than or equal to character <tt><i>b</i></tt>.
1360       *
1361       *    <p>
1362       *    <dt> <tt> [^<i>a</i>] </tt>
1363       *    <dd> Matches a single character that is not from character set or range
1364       *     <tt>{<i>a</i>}</tt>.  Note that the <tt>^</tt> character must occur
1365       *     immediately to the right of the opening bracket.
1366       *
1367       *    <p>
1368       *    <dt> <tt> \<i>c</i> </tt>
1369       *    <dd> Removes (escapes) any special meaning of character <i>c</i>.
1370       *
1371       *    <p>
1372       *    <dt> <tt> {ab,cd} </tt>
1373       *    <dd> Matches a string from the string set <tt>{<i>ab, cd</i>} </tt>
1374       *    
1375       *    <p>
1376       *    <dt> <tt> {ab,c{de,fh}} </tt>
1377       *    <dd> Matches a string from the string set <tt>{<i>ab, cde, cfh</i>}</tt>
1378       *
1379       *   </dl>
1380       *  </dd>
1381       * </dl>
1382       *
1383       * @param pathPattern a regular expression specifying a pth pattern
1384    
1385       * @return an array of paths that match the path pattern
1386       * @throws IOException
1387       */
1388      public FileStatus[] globStatus(Path pathPattern) throws IOException {
1389        return globStatus(pathPattern, DEFAULT_FILTER);
1390      }
1391      
1392      /**
1393       * Return an array of FileStatus objects whose path names match pathPattern
1394       * and is accepted by the user-supplied path filter. Results are sorted by
1395       * their path names.
1396       * Return null if pathPattern has no glob and the path does not exist.
1397       * Return an empty array if pathPattern has a glob and no path matches it. 
1398       * 
1399       * @param pathPattern
1400       *          a regular expression specifying the path pattern
1401       * @param filter
1402       *          a user-supplied path filter
1403       * @return an array of FileStatus objects
1404       * @throws IOException if any I/O error occurs when fetching file status
1405       */
1406      public FileStatus[] globStatus(Path pathPattern, PathFilter filter)
1407          throws IOException {
1408        String filename = pathPattern.toUri().getPath();
1409        List<String> filePatterns = GlobExpander.expand(filename);
1410        if (filePatterns.size() == 1) {
1411          return globStatusInternal(pathPattern, filter);
1412        } else {
1413          List<FileStatus> results = new ArrayList<FileStatus>();
1414          for (String filePattern : filePatterns) {
1415            FileStatus[] files = globStatusInternal(new Path(filePattern), filter);
1416            for (FileStatus file : files) {
1417              results.add(file);
1418            }
1419          }
1420          return results.toArray(new FileStatus[results.size()]);
1421        }
1422      }
1423    
1424      private FileStatus[] globStatusInternal(Path pathPattern, PathFilter filter)
1425          throws IOException {
1426        Path[] parents = new Path[1];
1427        int level = 0;
1428        String filename = pathPattern.toUri().getPath();
1429        
1430        // path has only zero component
1431        if ("".equals(filename) || Path.SEPARATOR.equals(filename)) {
1432          return getFileStatus(new Path[]{pathPattern});
1433        }
1434    
1435        // path has at least one component
1436        String[] components = filename.split(Path.SEPARATOR);
1437        // get the first component
1438        if (pathPattern.isAbsolute()) {
1439          parents[0] = new Path(Path.SEPARATOR);
1440          level = 1;
1441        } else {
1442          parents[0] = new Path(Path.CUR_DIR);
1443        }
1444    
1445        // glob the paths that match the parent path, i.e., [0, components.length-1]
1446        boolean[] hasGlob = new boolean[]{false};
1447        Path[] parentPaths = globPathsLevel(parents, components, level, hasGlob);
1448        FileStatus[] results;
1449        if (parentPaths == null || parentPaths.length == 0) {
1450          results = null;
1451        } else {
1452          // Now work on the last component of the path
1453          GlobFilter fp = new GlobFilter(components[components.length - 1], filter);
1454          if (fp.hasPattern()) { // last component has a pattern
1455            // list parent directories and then glob the results
1456            results = listStatus(parentPaths, fp);
1457            hasGlob[0] = true;
1458          } else { // last component does not have a pattern
1459            // get all the path names
1460            ArrayList<Path> filteredPaths = new ArrayList<Path>(parentPaths.length);
1461            for (int i = 0; i < parentPaths.length; i++) {
1462              parentPaths[i] = new Path(parentPaths[i],
1463                components[components.length - 1]);
1464              if (fp.accept(parentPaths[i])) {
1465                filteredPaths.add(parentPaths[i]);
1466              }
1467            }
1468            // get all their statuses
1469            results = getFileStatus(
1470                filteredPaths.toArray(new Path[filteredPaths.size()]));
1471          }
1472        }
1473    
1474        // Decide if the pathPattern contains a glob or not
1475        if (results == null) {
1476          if (hasGlob[0]) {
1477            results = new FileStatus[0];
1478          }
1479        } else {
1480          if (results.length == 0 ) {
1481            if (!hasGlob[0]) {
1482              results = null;
1483            }
1484          } else {
1485            Arrays.sort(results);
1486          }
1487        }
1488        return results;
1489      }
1490    
1491      /*
1492       * For a path of N components, return a list of paths that match the
1493       * components [<code>level</code>, <code>N-1</code>].
1494       */
1495      private Path[] globPathsLevel(Path[] parents, String[] filePattern,
1496          int level, boolean[] hasGlob) throws IOException {
1497        if (level == filePattern.length - 1)
1498          return parents;
1499        if (parents == null || parents.length == 0) {
1500          return null;
1501        }
1502        GlobFilter fp = new GlobFilter(filePattern[level]);
1503        if (fp.hasPattern()) {
1504          parents = FileUtil.stat2Paths(listStatus(parents, fp));
1505          hasGlob[0] = true;
1506        } else {
1507          for (int i = 0; i < parents.length; i++) {
1508            parents[i] = new Path(parents[i], filePattern[level]);
1509          }
1510        }
1511        return globPathsLevel(parents, filePattern, level + 1, hasGlob);
1512      }
1513    
1514      /**
1515       * List the statuses of the files/directories in the given path if the path is
1516       * a directory. 
1517       * Return the file's status and block locations If the path is a file.
1518       * 
1519       * If a returned status is a file, it contains the file's block locations.
1520       * 
1521       * @param f is the path
1522       *
1523       * @return an iterator that traverses statuses of the files/directories 
1524       *         in the given path
1525       *
1526       * @throws FileNotFoundException If <code>f</code> does not exist
1527       * @throws IOException If an I/O error occurred
1528       */
1529      public RemoteIterator<LocatedFileStatus> listLocatedStatus(final Path f)
1530      throws FileNotFoundException, IOException {
1531        return listLocatedStatus(f, DEFAULT_FILTER);
1532      }
1533    
1534      /**
1535       * Listing a directory
1536       * The returned results include its block location if it is a file
1537       * The results are filtered by the given path filter
1538       * @param f a path
1539       * @param filter a path filter
1540       * @return an iterator that traverses statuses of the files/directories 
1541       *         in the given path
1542       * @throws FileNotFoundException if <code>f</code> does not exist
1543       * @throws IOException if any I/O error occurred
1544       */
1545      protected RemoteIterator<LocatedFileStatus> listLocatedStatus(final Path f,
1546          final PathFilter filter)
1547      throws FileNotFoundException, IOException {
1548        return new RemoteIterator<LocatedFileStatus>() {
1549          private final FileStatus[] stats = listStatus(f, filter);
1550          private int i = 0;
1551    
1552          @Override
1553          public boolean hasNext() {
1554            return i<stats.length;
1555          }
1556    
1557          @Override
1558          public LocatedFileStatus next() throws IOException {
1559            if (!hasNext()) {
1560              throw new NoSuchElementException("No more entry in " + f);
1561            }
1562            FileStatus result = stats[i++];
1563            BlockLocation[] locs = result.isFile() ?
1564                getFileBlockLocations(result.getPath(), 0, result.getLen()) :
1565                null;
1566            return new LocatedFileStatus(result, locs);
1567          }
1568        };
1569      }
1570    
1571      /**
1572       * List the statuses and block locations of the files in the given path.
1573       * 
1574       * If the path is a directory, 
1575       *   if recursive is false, returns files in the directory;
1576       *   if recursive is true, return files in the subtree rooted at the path.
1577       * If the path is a file, return the file's status and block locations.
1578       * 
1579       * @param f is the path
1580       * @param recursive if the subdirectories need to be traversed recursively
1581       *
1582       * @return an iterator that traverses statuses of the files
1583       *
1584       * @throws FileNotFoundException when the path does not exist;
1585       *         IOException see specific implementation
1586       */
1587      public RemoteIterator<LocatedFileStatus> listFiles(
1588          final Path f, final boolean recursive)
1589      throws FileNotFoundException, IOException {
1590        return new RemoteIterator<LocatedFileStatus>() {
1591          private Stack<RemoteIterator<LocatedFileStatus>> itors = 
1592            new Stack<RemoteIterator<LocatedFileStatus>>();
1593          private RemoteIterator<LocatedFileStatus> curItor =
1594            listLocatedStatus(f);
1595          private LocatedFileStatus curFile;
1596         
1597          @Override
1598          public boolean hasNext() throws IOException {
1599            while (curFile == null) {
1600              if (curItor.hasNext()) {
1601                handleFileStat(curItor.next());
1602              } else if (!itors.empty()) {
1603                curItor = itors.pop();
1604              } else {
1605                return false;
1606              }
1607            }
1608            return true;
1609          }
1610    
1611          /**
1612           * Process the input stat.
1613           * If it is a file, return the file stat.
1614           * If it is a directory, traverse the directory if recursive is true;
1615           * ignore it if recursive is false.
1616           * @param stat input status
1617           * @throws IOException if any IO error occurs
1618           */
1619          private void handleFileStat(LocatedFileStatus stat) throws IOException {
1620            if (stat.isFile()) { // file
1621              curFile = stat;
1622            } else if (recursive) { // directory
1623              itors.push(curItor);
1624              curItor = listLocatedStatus(stat.getPath());
1625            }
1626          }
1627    
1628          @Override
1629          public LocatedFileStatus next() throws IOException {
1630            if (hasNext()) {
1631              LocatedFileStatus result = curFile;
1632              curFile = null;
1633              return result;
1634            } 
1635            throw new java.util.NoSuchElementException("No more entry in " + f);
1636          }
1637        };
1638      }
1639      
1640      /** Return the current user's home directory in this filesystem.
1641       * The default implementation returns "/user/$USER/".
1642       */
1643      public Path getHomeDirectory() {
1644        return this.makeQualified(
1645            new Path("/user/"+System.getProperty("user.name")));
1646      }
1647    
1648    
1649      /**
1650       * Set the current working directory for the given file system. All relative
1651       * paths will be resolved relative to it.
1652       * 
1653       * @param new_dir
1654       */
1655      public abstract void setWorkingDirectory(Path new_dir);
1656        
1657      /**
1658       * Get the current working directory for the given file system
1659       * @return the directory pathname
1660       */
1661      public abstract Path getWorkingDirectory();
1662      
1663      
1664      /**
1665       * Note: with the new FilesContext class, getWorkingDirectory()
1666       * will be removed. 
1667       * The working directory is implemented in FilesContext.
1668       * 
1669       * Some file systems like LocalFileSystem have an initial workingDir
1670       * that we use as the starting workingDir. For other file systems
1671       * like HDFS there is no built in notion of an inital workingDir.
1672       * 
1673       * @return if there is built in notion of workingDir then it
1674       * is returned; else a null is returned.
1675       */
1676      protected Path getInitialWorkingDirectory() {
1677        return null;
1678      }
1679    
1680      /**
1681       * Call {@link #mkdirs(Path, FsPermission)} with default permission.
1682       */
1683      public boolean mkdirs(Path f) throws IOException {
1684        return mkdirs(f, FsPermission.getDefault());
1685      }
1686    
1687      /**
1688       * Make the given file and all non-existent parents into
1689       * directories. Has the semantics of Unix 'mkdir -p'.
1690       * Existence of the directory hierarchy is not an error.
1691       * @param f path to create
1692       * @param permission to apply to f
1693       */
1694      public abstract boolean mkdirs(Path f, FsPermission permission
1695          ) throws IOException;
1696    
1697      /**
1698       * The src file is on the local disk.  Add it to FS at
1699       * the given dst name and the source is kept intact afterwards
1700       * @param src path
1701       * @param dst path
1702       */
1703      public void copyFromLocalFile(Path src, Path dst)
1704        throws IOException {
1705        copyFromLocalFile(false, src, dst);
1706      }
1707    
1708      /**
1709       * The src files is on the local disk.  Add it to FS at
1710       * the given dst name, removing the source afterwards.
1711       * @param srcs path
1712       * @param dst path
1713       */
1714      public void moveFromLocalFile(Path[] srcs, Path dst)
1715        throws IOException {
1716        copyFromLocalFile(true, true, srcs, dst);
1717      }
1718    
1719      /**
1720       * The src file is on the local disk.  Add it to FS at
1721       * the given dst name, removing the source afterwards.
1722       * @param src path
1723       * @param dst path
1724       */
1725      public void moveFromLocalFile(Path src, Path dst)
1726        throws IOException {
1727        copyFromLocalFile(true, src, dst);
1728      }
1729    
1730      /**
1731       * The src file is on the local disk.  Add it to FS at
1732       * the given dst name.
1733       * delSrc indicates if the source should be removed
1734       * @param delSrc whether to delete the src
1735       * @param src path
1736       * @param dst path
1737       */
1738      public void copyFromLocalFile(boolean delSrc, Path src, Path dst)
1739        throws IOException {
1740        copyFromLocalFile(delSrc, true, src, dst);
1741      }
1742      
1743      /**
1744       * The src files are on the local disk.  Add it to FS at
1745       * the given dst name.
1746       * delSrc indicates if the source should be removed
1747       * @param delSrc whether to delete the src
1748       * @param overwrite whether to overwrite an existing file
1749       * @param srcs array of paths which are source
1750       * @param dst path
1751       */
1752      public void copyFromLocalFile(boolean delSrc, boolean overwrite, 
1753                                    Path[] srcs, Path dst)
1754        throws IOException {
1755        Configuration conf = getConf();
1756        FileUtil.copy(getLocal(conf), srcs, this, dst, delSrc, overwrite, conf);
1757      }
1758      
1759      /**
1760       * The src file is on the local disk.  Add it to FS at
1761       * the given dst name.
1762       * delSrc indicates if the source should be removed
1763       * @param delSrc whether to delete the src
1764       * @param overwrite whether to overwrite an existing file
1765       * @param src path
1766       * @param dst path
1767       */
1768      public void copyFromLocalFile(boolean delSrc, boolean overwrite, 
1769                                    Path src, Path dst)
1770        throws IOException {
1771        Configuration conf = getConf();
1772        FileUtil.copy(getLocal(conf), src, this, dst, delSrc, overwrite, conf);
1773      }
1774        
1775      /**
1776       * The src file is under FS, and the dst is on the local disk.
1777       * Copy it from FS control to the local dst name.
1778       * @param src path
1779       * @param dst path
1780       */
1781      public void copyToLocalFile(Path src, Path dst) throws IOException {
1782        copyToLocalFile(false, src, dst);
1783      }
1784        
1785      /**
1786       * The src file is under FS, and the dst is on the local disk.
1787       * Copy it from FS control to the local dst name.
1788       * Remove the source afterwards
1789       * @param src path
1790       * @param dst path
1791       */
1792      public void moveToLocalFile(Path src, Path dst) throws IOException {
1793        copyToLocalFile(true, src, dst);
1794      }
1795    
1796      /**
1797       * The src file is under FS, and the dst is on the local disk.
1798       * Copy it from FS control to the local dst name.
1799       * delSrc indicates if the src will be removed or not.
1800       * @param delSrc whether to delete the src
1801       * @param src path
1802       * @param dst path
1803       */   
1804      public void copyToLocalFile(boolean delSrc, Path src, Path dst)
1805        throws IOException {
1806        copyToLocalFile(delSrc, src, dst, false);
1807      }
1808      
1809        /**
1810       * The src file is under FS, and the dst is on the local disk. Copy it from FS
1811       * control to the local dst name. delSrc indicates if the src will be removed
1812       * or not. useRawLocalFileSystem indicates whether to use RawLocalFileSystem
1813       * as local file system or not. RawLocalFileSystem is non crc file system.So,
1814       * It will not create any crc files at local.
1815       * 
1816       * @param delSrc
1817       *          whether to delete the src
1818       * @param src
1819       *          path
1820       * @param dst
1821       *          path
1822       * @param useRawLocalFileSystem
1823       *          whether to use RawLocalFileSystem as local file system or not.
1824       * 
1825       * @throws IOException
1826       *           - if any IO error
1827       */
1828      public void copyToLocalFile(boolean delSrc, Path src, Path dst,
1829          boolean useRawLocalFileSystem) throws IOException {
1830        Configuration conf = getConf();
1831        FileSystem local = null;
1832        if (useRawLocalFileSystem) {
1833          local = getLocal(conf).getRawFileSystem();
1834        } else {
1835          local = getLocal(conf);
1836        }
1837        FileUtil.copy(this, src, local, dst, delSrc, conf);
1838      }
1839    
1840      /**
1841       * Returns a local File that the user can write output to.  The caller
1842       * provides both the eventual FS target name and the local working
1843       * file.  If the FS is local, we write directly into the target.  If
1844       * the FS is remote, we write into the tmp local area.
1845       * @param fsOutputFile path of output file
1846       * @param tmpLocalFile path of local tmp file
1847       */
1848      public Path startLocalOutput(Path fsOutputFile, Path tmpLocalFile)
1849        throws IOException {
1850        return tmpLocalFile;
1851      }
1852    
1853      /**
1854       * Called when we're all done writing to the target.  A local FS will
1855       * do nothing, because we've written to exactly the right place.  A remote
1856       * FS will copy the contents of tmpLocalFile to the correct target at
1857       * fsOutputFile.
1858       * @param fsOutputFile path of output file
1859       * @param tmpLocalFile path to local tmp file
1860       */
1861      public void completeLocalOutput(Path fsOutputFile, Path tmpLocalFile)
1862        throws IOException {
1863        moveFromLocalFile(tmpLocalFile, fsOutputFile);
1864      }
1865    
1866      /**
1867       * No more filesystem operations are needed.  Will
1868       * release any held locks.
1869       */
1870      public void close() throws IOException {
1871        // delete all files that were marked as delete-on-exit.
1872        processDeleteOnExit();
1873        CACHE.remove(this.key, this);
1874      }
1875    
1876      /** Return the total size of all files in the filesystem.*/
1877      public long getUsed() throws IOException{
1878        long used = 0;
1879        FileStatus[] files = listStatus(new Path("/"));
1880        for(FileStatus file:files){
1881          used += file.getLen();
1882        }
1883        return used;
1884      }
1885      
1886      /**
1887       * Get the block size for a particular file.
1888       * @param f the filename
1889       * @return the number of bytes in a block
1890       */
1891      /** @deprecated Use getFileStatus() instead */
1892      @Deprecated
1893      public long getBlockSize(Path f) throws IOException {
1894        return getFileStatus(f).getBlockSize();
1895      }
1896    
1897      /** Return the number of bytes that large input files should be optimally
1898       * be split into to minimize i/o time. */
1899      public long getDefaultBlockSize() {
1900        // default to 32MB: large enough to minimize the impact of seeks
1901        return getConf().getLong("fs.local.block.size", 32 * 1024 * 1024);
1902      }
1903        
1904      /**
1905       * Get the default replication.
1906       */
1907      public short getDefaultReplication() { return 1; }
1908    
1909      /**
1910       * Return a file status object that represents the path.
1911       * @param f The path we want information from
1912       * @return a FileStatus object
1913       * @throws FileNotFoundException when the path does not exist;
1914       *         IOException see specific implementation
1915       */
1916      public abstract FileStatus getFileStatus(Path f) throws IOException;
1917    
1918      /**
1919       * Get the checksum of a file.
1920       *
1921       * @param f The file path
1922       * @return The file checksum.  The default return value is null,
1923       *  which indicates that no checksum algorithm is implemented
1924       *  in the corresponding FileSystem.
1925       */
1926      public FileChecksum getFileChecksum(Path f) throws IOException {
1927        return null;
1928      }
1929      
1930      /**
1931       * Set the verify checksum flag. This is only applicable if the 
1932       * corresponding FileSystem supports checksum. By default doesn't do anything.
1933       * @param verifyChecksum
1934       */
1935      public void setVerifyChecksum(boolean verifyChecksum) {
1936        //doesn't do anything
1937      }
1938    
1939      /**
1940       * Return a list of file status objects that corresponds to the list of paths
1941       * excluding those non-existent paths.
1942       * 
1943       * @param paths
1944       *          the list of paths we want information from
1945       * @return a list of FileStatus objects
1946       * @throws IOException
1947       *           see specific implementation
1948       */
1949      private FileStatus[] getFileStatus(Path[] paths) throws IOException {
1950        if (paths == null) {
1951          return null;
1952        }
1953        ArrayList<FileStatus> results = new ArrayList<FileStatus>(paths.length);
1954        for (int i = 0; i < paths.length; i++) {
1955          try {
1956            results.add(getFileStatus(paths[i]));
1957          } catch (FileNotFoundException e) { // do nothing
1958          }
1959        }
1960        return results.toArray(new FileStatus[results.size()]);
1961      }
1962      
1963      /**
1964       * Returns a status object describing the use and capacity of the
1965       * file system. If the file system has multiple partitions, the
1966       * use and capacity of the root partition is reflected.
1967       * 
1968       * @return a FsStatus object
1969       * @throws IOException
1970       *           see specific implementation
1971       */
1972      public FsStatus getStatus() throws IOException {
1973        return getStatus(null);
1974      }
1975    
1976      /**
1977       * Returns a status object describing the use and capacity of the
1978       * file system. If the file system has multiple partitions, the
1979       * use and capacity of the partition pointed to by the specified
1980       * path is reflected.
1981       * @param p Path for which status should be obtained. null means
1982       * the default partition. 
1983       * @return a FsStatus object
1984       * @throws IOException
1985       *           see specific implementation
1986       */
1987      public FsStatus getStatus(Path p) throws IOException {
1988        return new FsStatus(Long.MAX_VALUE, 0, Long.MAX_VALUE);
1989      }
1990    
1991      /**
1992       * Set permission of a path.
1993       * @param p
1994       * @param permission
1995       */
1996      public void setPermission(Path p, FsPermission permission
1997          ) throws IOException {
1998      }
1999    
2000      /**
2001       * Set owner of a path (i.e. a file or a directory).
2002       * The parameters username and groupname cannot both be null.
2003       * @param p The path
2004       * @param username If it is null, the original username remains unchanged.
2005       * @param groupname If it is null, the original groupname remains unchanged.
2006       */
2007      public void setOwner(Path p, String username, String groupname
2008          ) throws IOException {
2009      }
2010    
2011      /**
2012       * Set access time of a file
2013       * @param p The path
2014       * @param mtime Set the modification time of this file.
2015       *              The number of milliseconds since Jan 1, 1970. 
2016       *              A value of -1 means that this call should not set modification time.
2017       * @param atime Set the access time of this file.
2018       *              The number of milliseconds since Jan 1, 1970. 
2019       *              A value of -1 means that this call should not set access time.
2020       */
2021      public void setTimes(Path p, long mtime, long atime
2022          ) throws IOException {
2023      }
2024    
2025      private static FileSystem createFileSystem(URI uri, Configuration conf
2026          ) throws IOException {
2027        Class<?> clazz = conf.getClass("fs." + uri.getScheme() + ".impl", null);
2028        if (clazz == null) {
2029          throw new IOException("No FileSystem for scheme: " + uri.getScheme());
2030        }
2031        FileSystem fs = (FileSystem)ReflectionUtils.newInstance(clazz, conf);
2032        fs.initialize(uri, conf);
2033        return fs;
2034      }
2035    
2036      /** Caching FileSystem objects */
2037      static class Cache {
2038        private final ClientFinalizer clientFinalizer = new ClientFinalizer();
2039    
2040        private final Map<Key, FileSystem> map = new HashMap<Key, FileSystem>();
2041        private final Set<Key> toAutoClose = new HashSet<Key>();
2042    
2043        /** A variable that makes all objects in the cache unique */
2044        private static AtomicLong unique = new AtomicLong(1);
2045    
2046        FileSystem get(URI uri, Configuration conf) throws IOException{
2047          Key key = new Key(uri, conf);
2048          return getInternal(uri, conf, key);
2049        }
2050    
2051        /** The objects inserted into the cache using this method are all unique */
2052        FileSystem getUnique(URI uri, Configuration conf) throws IOException{
2053          Key key = new Key(uri, conf, unique.getAndIncrement());
2054          return getInternal(uri, conf, key);
2055        }
2056    
2057        private FileSystem getInternal(URI uri, Configuration conf, Key key) throws IOException{
2058          FileSystem fs;
2059          synchronized (this) {
2060            fs = map.get(key);
2061          }
2062          if (fs != null) {
2063            return fs;
2064          }
2065    
2066          fs = createFileSystem(uri, conf);
2067          synchronized (this) { // refetch the lock again
2068            FileSystem oldfs = map.get(key);
2069            if (oldfs != null) { // a file system is created while lock is releasing
2070              fs.close(); // close the new file system
2071              return oldfs;  // return the old file system
2072            }
2073            
2074            // now insert the new file system into the map
2075            if (map.isEmpty() && !clientFinalizer.isAlive()) {
2076              Runtime.getRuntime().addShutdownHook(clientFinalizer);
2077            }
2078            fs.key = key;
2079            map.put(key, fs);
2080            if (conf.getBoolean("fs.automatic.close", true)) {
2081              toAutoClose.add(key);
2082            }
2083            return fs;
2084          }
2085        }
2086    
2087        synchronized void remove(Key key, FileSystem fs) {
2088          if (map.containsKey(key) && fs == map.get(key)) {
2089            map.remove(key);
2090            toAutoClose.remove(key);
2091            if (map.isEmpty() && !clientFinalizer.isAlive()) {
2092              if (!Runtime.getRuntime().removeShutdownHook(clientFinalizer)) {
2093                LOG.info("Could not cancel cleanup thread, though no " +
2094                         "FileSystems are open");
2095              }
2096            }
2097          }
2098        }
2099    
2100        synchronized void closeAll() throws IOException {
2101          closeAll(false);
2102        }
2103    
2104        /**
2105         * Close all FileSystem instances in the Cache.
2106         * @param onlyAutomatic only close those that are marked for automatic closing
2107         */
2108        synchronized void closeAll(boolean onlyAutomatic) throws IOException {
2109          List<IOException> exceptions = new ArrayList<IOException>();
2110    
2111          // Make a copy of the keys in the map since we'll be modifying
2112          // the map while iterating over it, which isn't safe.
2113          List<Key> keys = new ArrayList<Key>();
2114          keys.addAll(map.keySet());
2115    
2116          for (Key key : keys) {
2117            final FileSystem fs = map.get(key);
2118    
2119            if (onlyAutomatic && !toAutoClose.contains(key)) {
2120              continue;
2121            }
2122    
2123            //remove from cache
2124            remove(key, fs);
2125    
2126            if (fs != null) {
2127              try {
2128                fs.close();
2129              }
2130              catch(IOException ioe) {
2131                exceptions.add(ioe);
2132              }
2133            }
2134          }
2135    
2136          if (!exceptions.isEmpty()) {
2137            throw MultipleIOException.createIOException(exceptions);
2138          }
2139        }
2140    
2141        private class ClientFinalizer extends Thread {
2142          public synchronized void run() {
2143            try {
2144              closeAll(true);
2145            } catch (IOException e) {
2146              LOG.info("FileSystem.Cache.closeAll() threw an exception:\n" + e);
2147            }
2148          }
2149        }
2150    
2151        synchronized void closeAll(UserGroupInformation ugi) throws IOException {
2152          List<FileSystem> targetFSList = new ArrayList<FileSystem>();
2153          //Make a pass over the list and collect the filesystems to close
2154          //we cannot close inline since close() removes the entry from the Map
2155          for (Map.Entry<Key, FileSystem> entry : map.entrySet()) {
2156            final Key key = entry.getKey();
2157            final FileSystem fs = entry.getValue();
2158            if (ugi.equals(key.ugi) && fs != null) {
2159              targetFSList.add(fs);   
2160            }
2161          }
2162          List<IOException> exceptions = new ArrayList<IOException>();
2163          //now make a pass over the target list and close each
2164          for (FileSystem fs : targetFSList) {
2165            try {
2166              fs.close();
2167            }
2168            catch(IOException ioe) {
2169              exceptions.add(ioe);
2170            }
2171          }
2172          if (!exceptions.isEmpty()) {
2173            throw MultipleIOException.createIOException(exceptions);
2174          }
2175        }
2176    
2177        /** FileSystem.Cache.Key */
2178        static class Key {
2179          final String scheme;
2180          final String authority;
2181          final UserGroupInformation ugi;
2182          final long unique;   // an artificial way to make a key unique
2183    
2184          Key(URI uri, Configuration conf) throws IOException {
2185            this(uri, conf, 0);
2186          }
2187    
2188          Key(URI uri, Configuration conf, long unique) throws IOException {
2189            scheme = uri.getScheme()==null?"":uri.getScheme().toLowerCase();
2190            authority = uri.getAuthority()==null?"":uri.getAuthority().toLowerCase();
2191            this.unique = unique;
2192            
2193            this.ugi = UserGroupInformation.getCurrentUser();
2194          }
2195    
2196          /** {@inheritDoc} */
2197          public int hashCode() {
2198            return (scheme + authority).hashCode() + ugi.hashCode() + (int)unique;
2199          }
2200    
2201          static boolean isEqual(Object a, Object b) {
2202            return a == b || (a != null && a.equals(b));        
2203          }
2204    
2205          /** {@inheritDoc} */
2206          public boolean equals(Object obj) {
2207            if (obj == this) {
2208              return true;
2209            }
2210            if (obj != null && obj instanceof Key) {
2211              Key that = (Key)obj;
2212              return isEqual(this.scheme, that.scheme)
2213                     && isEqual(this.authority, that.authority)
2214                     && isEqual(this.ugi, that.ugi)
2215                     && (this.unique == that.unique);
2216            }
2217            return false;        
2218          }
2219    
2220          /** {@inheritDoc} */
2221          public String toString() {
2222            return "("+ugi.toString() + ")@" + scheme + "://" + authority;        
2223          }
2224        }
2225      }
2226      
2227      public static final class Statistics {
2228        private final String scheme;
2229        private AtomicLong bytesRead = new AtomicLong();
2230        private AtomicLong bytesWritten = new AtomicLong();
2231        private AtomicInteger readOps = new AtomicInteger();
2232        private AtomicInteger largeReadOps = new AtomicInteger();
2233        private AtomicInteger writeOps = new AtomicInteger();
2234        
2235        public Statistics(String scheme) {
2236          this.scheme = scheme;
2237        }
2238    
2239        /**
2240         * Copy constructor.
2241         * 
2242         * @param st
2243         *          The input Statistics object which is cloned.
2244         */
2245        public Statistics(Statistics st) {
2246          this.scheme = st.scheme;
2247          this.bytesRead = new AtomicLong(st.bytesRead.longValue());
2248          this.bytesWritten = new AtomicLong(st.bytesWritten.longValue());
2249        }
2250    
2251        /**
2252         * Increment the bytes read in the statistics
2253         * @param newBytes the additional bytes read
2254         */
2255        public void incrementBytesRead(long newBytes) {
2256          bytesRead.getAndAdd(newBytes);
2257        }
2258        
2259        /**
2260         * Increment the bytes written in the statistics
2261         * @param newBytes the additional bytes written
2262         */
2263        public void incrementBytesWritten(long newBytes) {
2264          bytesWritten.getAndAdd(newBytes);
2265        }
2266        
2267        /**
2268         * Increment the number of read operations
2269         * @param count number of read operations
2270         */
2271        public void incrementReadOps(int count) {
2272          readOps.getAndAdd(count);
2273        }
2274    
2275        /**
2276         * Increment the number of large read operations
2277         * @param count number of large read operations
2278         */
2279        public void incrementLargeReadOps(int count) {
2280          largeReadOps.getAndAdd(count);
2281        }
2282    
2283        /**
2284         * Increment the number of write operations
2285         * @param count number of write operations
2286         */
2287        public void incrementWriteOps(int count) {
2288          writeOps.getAndAdd(count);
2289        }
2290    
2291        /**
2292         * Get the total number of bytes read
2293         * @return the number of bytes
2294         */
2295        public long getBytesRead() {
2296          return bytesRead.get();
2297        }
2298        
2299        /**
2300         * Get the total number of bytes written
2301         * @return the number of bytes
2302         */
2303        public long getBytesWritten() {
2304          return bytesWritten.get();
2305        }
2306        
2307        /**
2308         * Get the number of file system read operations such as list files
2309         * @return number of read operations
2310         */
2311        public int getReadOps() {
2312          return readOps.get() + largeReadOps.get();
2313        }
2314    
2315        /**
2316         * Get the number of large file system read operations such as list files
2317         * under a large directory
2318         * @return number of large read operations
2319         */
2320        public int getLargeReadOps() {
2321          return largeReadOps.get();
2322        }
2323    
2324        /**
2325         * Get the number of file system write operations such as create, append 
2326         * rename etc.
2327         * @return number of write operations
2328         */
2329        public int getWriteOps() {
2330          return writeOps.get();
2331        }
2332    
2333        public String toString() {
2334          return bytesRead + " bytes read, " + bytesWritten + " bytes written, "
2335              + readOps + " read ops, " + largeReadOps + " large read ops, "
2336              + writeOps + " write ops";
2337        }
2338        
2339        /**
2340         * Reset the counts of bytes to 0.
2341         */
2342        public void reset() {
2343          bytesWritten.set(0);
2344          bytesRead.set(0);
2345        }
2346        
2347        /**
2348         * Get the uri scheme associated with this statistics object.
2349         * @return the schema associated with this set of statistics
2350         */
2351        public String getScheme() {
2352          return scheme;
2353        }
2354      }
2355      
2356      /**
2357       * Get the Map of Statistics object indexed by URI Scheme.
2358       * @return a Map having a key as URI scheme and value as Statistics object
2359       * @deprecated use {@link #getAllStatistics} instead
2360       */
2361      @Deprecated
2362      public static synchronized Map<String, Statistics> getStatistics() {
2363        Map<String, Statistics> result = new HashMap<String, Statistics>();
2364        for(Statistics stat: statisticsTable.values()) {
2365          result.put(stat.getScheme(), stat);
2366        }
2367        return result;
2368      }
2369    
2370      /**
2371       * Return the FileSystem classes that have Statistics
2372       */
2373      public static synchronized List<Statistics> getAllStatistics() {
2374        return new ArrayList<Statistics>(statisticsTable.values());
2375      }
2376      
2377      /**
2378       * Get the statistics for a particular file system
2379       * @param cls the class to lookup
2380       * @return a statistics object
2381       */
2382      public static synchronized 
2383      Statistics getStatistics(String scheme, Class<? extends FileSystem> cls) {
2384        Statistics result = statisticsTable.get(cls);
2385        if (result == null) {
2386          result = new Statistics(scheme);
2387          statisticsTable.put(cls, result);
2388        }
2389        return result;
2390      }
2391      
2392      /**
2393       * Reset all statistics for all file systems
2394       */
2395      public static synchronized void clearStatistics() {
2396        for(Statistics stat: statisticsTable.values()) {
2397          stat.reset();
2398        }
2399      }
2400    
2401      /**
2402       * Print all statistics for all file systems
2403       */
2404      public static synchronized
2405      void printStatistics() throws IOException {
2406        for (Map.Entry<Class<? extends FileSystem>, Statistics> pair: 
2407                statisticsTable.entrySet()) {
2408          System.out.println("  FileSystem " + pair.getKey().getName() + 
2409                             ": " + pair.getValue());
2410        }
2411      }
2412    }