HBASE-24163 MOB compactor implementations should use format specifiers when calling...
[hbase.git] / hbase-common / src / main / java / org / apache / hadoop / hbase / util / CommonFSUtils.java
blob98dc5403345dd632234d9b9b55b0925c32ce1919
1 /*
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 package org.apache.hadoop.hbase.util;
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.net.URI;
26 import java.net.URISyntaxException;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30 import java.util.concurrent.ConcurrentHashMap;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.fs.FSDataOutputStream;
33 import org.apache.hadoop.fs.FSDataOutputStreamBuilder;
34 import org.apache.hadoop.fs.FileStatus;
35 import org.apache.hadoop.fs.FileSystem;
36 import org.apache.hadoop.fs.LocalFileSystem;
37 import org.apache.hadoop.fs.LocatedFileStatus;
38 import org.apache.hadoop.fs.Path;
39 import org.apache.hadoop.fs.PathFilter;
40 import org.apache.hadoop.fs.RemoteIterator;
41 import org.apache.hadoop.fs.permission.FsPermission;
42 import org.apache.hadoop.hbase.HConstants;
43 import org.apache.hadoop.hbase.TableName;
44 import org.apache.yetus.audience.InterfaceAudience;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47 import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
48 import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
50 /**
51 * Utility methods for interacting with the underlying file system.
52 * <p/>
53 * Note that {@link #setStoragePolicy(FileSystem, Path, String)} is tested in TestFSUtils and
54 * pre-commit will run the hbase-server tests if there's code change in this class. See
55 * <a href="https://issues.apache.org/jira/browse/HBASE-20838">HBASE-20838</a> for more details.
57 @InterfaceAudience.Private
58 public abstract class CommonFSUtils {
59 private static final Logger LOG = LoggerFactory.getLogger(CommonFSUtils.class);
61 /** Parameter name for HBase WAL directory */
62 public static final String HBASE_WAL_DIR = "hbase.wal.dir";
64 /** Parameter to disable stream capability enforcement checks */
65 public static final String UNSAFE_STREAM_CAPABILITY_ENFORCE =
66 "hbase.unsafe.stream.capability.enforce";
68 /** Full access permissions (starting point for a umask) */
69 public static final String FULL_RWX_PERMISSIONS = "777";
71 protected CommonFSUtils() {
72 super();
75 /**
76 * Compare of path component. Does not consider schema; i.e. if schemas
77 * different but <code>path</code> starts with <code>rootPath</code>,
78 * then the function returns true
79 * @param rootPath value to check for
80 * @param path subject to check
81 * @return True if <code>path</code> starts with <code>rootPath</code>
83 public static boolean isStartingWithPath(final Path rootPath, final String path) {
84 String uriRootPath = rootPath.toUri().getPath();
85 String tailUriPath = (new Path(path)).toUri().getPath();
86 return tailUriPath.startsWith(uriRootPath);
89 /**
90 * Compare path component of the Path URI; e.g. if hdfs://a/b/c and /a/b/c, it will compare the
91 * '/a/b/c' part. Does not consider schema; i.e. if schemas different but path or subpath matches,
92 * the two will equate.
93 * @param pathToSearch Path we will be trying to match against.
94 * @param pathTail what to match
95 * @return True if <code>pathTail</code> is tail on the path of <code>pathToSearch</code>
97 public static boolean isMatchingTail(final Path pathToSearch, String pathTail) {
98 return isMatchingTail(pathToSearch, new Path(pathTail));
102 * Compare path component of the Path URI; e.g. if hdfs://a/b/c and /a/b/c, it will compare the
103 * '/a/b/c' part. If you passed in 'hdfs://a/b/c and b/c, it would return true. Does not consider
104 * schema; i.e. if schemas different but path or subpath matches, the two will equate.
105 * @param pathToSearch Path we will be trying to match agains against
106 * @param pathTail what to match
107 * @return True if <code>pathTail</code> is tail on the path of <code>pathToSearch</code>
109 public static boolean isMatchingTail(final Path pathToSearch, final Path pathTail) {
110 if (pathToSearch.depth() != pathTail.depth()) {
111 return false;
113 Path tailPath = pathTail;
114 String tailName;
115 Path toSearch = pathToSearch;
116 String toSearchName;
117 boolean result = false;
118 do {
119 tailName = tailPath.getName();
120 if (tailName == null || tailName.length() <= 0) {
121 result = true;
122 break;
124 toSearchName = toSearch.getName();
125 if (toSearchName == null || toSearchName.length() <= 0) {
126 break;
128 // Move up a parent on each path for next go around. Path doesn't let us go off the end.
129 tailPath = tailPath.getParent();
130 toSearch = toSearch.getParent();
131 } while(tailName.equals(toSearchName));
132 return result;
136 * Delete if exists.
137 * @param fs filesystem object
138 * @param dir directory to delete
139 * @return True if deleted <code>dir</code>
140 * @throws IOException e
142 public static boolean deleteDirectory(final FileSystem fs, final Path dir) throws IOException {
143 return fs.exists(dir) && fs.delete(dir, true);
147 * Return the number of bytes that large input files should be optimally
148 * be split into to minimize i/o time.
150 * @param fs filesystem object
151 * @return the default block size for the path's filesystem
153 public static long getDefaultBlockSize(final FileSystem fs, final Path path) {
154 return fs.getDefaultBlockSize(path);
158 * Get the default replication.
160 * @param fs filesystem object
161 * @param f path of file
162 * @return default replication for the path's filesystem
164 public static short getDefaultReplication(final FileSystem fs, final Path path) {
165 return fs.getDefaultReplication(path);
169 * Returns the default buffer size to use during writes.
171 * The size of the buffer should probably be a multiple of hardware
172 * page size (4096 on Intel x86), and it determines how much data is
173 * buffered during read and write operations.
175 * @param fs filesystem object
176 * @return default buffer size to use during writes
178 public static int getDefaultBufferSize(final FileSystem fs) {
179 return fs.getConf().getInt("io.file.buffer.size", 4096);
183 * Create the specified file on the filesystem. By default, this will:
184 * <ol>
185 * <li>apply the umask in the configuration (if it is enabled)</li>
186 * <li>use the fs configured buffer size (or 4096 if not set)</li>
187 * <li>use the default replication</li>
188 * <li>use the default block size</li>
189 * <li>not track progress</li>
190 * </ol>
192 * @param fs {@link FileSystem} on which to write the file
193 * @param path {@link Path} to the file to write
194 * @param perm intial permissions
195 * @param overwrite Whether or not the created file should be overwritten.
196 * @return output stream to the created file
197 * @throws IOException if the file cannot be created
199 public static FSDataOutputStream create(FileSystem fs, Path path,
200 FsPermission perm, boolean overwrite) throws IOException {
201 if (LOG.isTraceEnabled()) {
202 LOG.trace("Creating file={} with permission={}, overwrite={}", path, perm, overwrite);
204 return fs.create(path, perm, overwrite, getDefaultBufferSize(fs),
205 getDefaultReplication(fs, path), getDefaultBlockSize(fs, path), null);
209 * Get the file permissions specified in the configuration, if they are
210 * enabled.
212 * @param fs filesystem that the file will be created on.
213 * @param conf configuration to read for determining if permissions are
214 * enabled and which to use
215 * @param permssionConfKey property key in the configuration to use when
216 * finding the permission
217 * @return the permission to use when creating a new file on the fs. If
218 * special permissions are not specified in the configuration, then
219 * the default permissions on the the fs will be returned.
221 public static FsPermission getFilePermissions(final FileSystem fs,
222 final Configuration conf, final String permssionConfKey) {
223 boolean enablePermissions = conf.getBoolean(
224 HConstants.ENABLE_DATA_FILE_UMASK, false);
226 if (enablePermissions) {
227 try {
228 FsPermission perm = new FsPermission(FULL_RWX_PERMISSIONS);
229 // make sure that we have a mask, if not, go default.
230 String mask = conf.get(permssionConfKey);
231 if (mask == null) {
232 return FsPermission.getFileDefault();
234 // appy the umask
235 FsPermission umask = new FsPermission(mask);
236 return perm.applyUMask(umask);
237 } catch (IllegalArgumentException e) {
238 LOG.warn(
239 "Incorrect umask attempted to be created: "
240 + conf.get(permssionConfKey)
241 + ", using default file permissions.", e);
242 return FsPermission.getFileDefault();
245 return FsPermission.getFileDefault();
249 * Verifies root directory path is a valid URI with a scheme
251 * @param root root directory path
252 * @return Passed <code>root</code> argument.
253 * @throws IOException if not a valid URI with a scheme
255 public static Path validateRootPath(Path root) throws IOException {
256 try {
257 URI rootURI = new URI(root.toString());
258 String scheme = rootURI.getScheme();
259 if (scheme == null) {
260 throw new IOException("Root directory does not have a scheme");
262 return root;
263 } catch (URISyntaxException e) {
264 throw new IOException("Root directory path is not a valid " +
265 "URI -- check your " + HConstants.HBASE_DIR + " configuration", e);
270 * Checks for the presence of the WAL log root path (using the provided conf object) in the given
271 * path. If it exists, this method removes it and returns the String representation of remaining
272 * relative path.
273 * @param path must not be null
274 * @param conf must not be null
275 * @return String representation of the remaining relative path
276 * @throws IOException from underlying filesystem
278 public static String removeWALRootPath(Path path, final Configuration conf) throws IOException {
279 Path root = getWALRootDir(conf);
280 String pathStr = path.toString();
281 // check that the path is absolute... it has the root path in it.
282 if (!pathStr.startsWith(root.toString())) {
283 return pathStr;
285 // if not, return as it is.
286 return pathStr.substring(root.toString().length() + 1);// remove the "/" too.
290 * Return the 'path' component of a Path. In Hadoop, Path is a URI. This
291 * method returns the 'path' component of a Path's URI: e.g. If a Path is
292 * <code>hdfs://example.org:9000/hbase_trunk/TestTable/compaction.dir</code>,
293 * this method returns <code>/hbase_trunk/TestTable/compaction.dir</code>.
294 * This method is useful if you want to print out a Path without qualifying
295 * Filesystem instance.
296 * @param p Filesystem Path whose 'path' component we are to return.
297 * @return Path portion of the Filesystem
299 public static String getPath(Path p) {
300 return p.toUri().getPath();
304 * @param c configuration
305 * @return {@link Path} to hbase root directory from
306 * configuration as a qualified Path.
307 * @throws IOException e
309 public static Path getRootDir(final Configuration c) throws IOException {
310 Path p = new Path(c.get(HConstants.HBASE_DIR));
311 FileSystem fs = p.getFileSystem(c);
312 return p.makeQualified(fs.getUri(), fs.getWorkingDirectory());
315 public static void setRootDir(final Configuration c, final Path root) {
316 c.set(HConstants.HBASE_DIR, root.toString());
319 public static void setFsDefault(final Configuration c, final Path root) {
320 c.set("fs.defaultFS", root.toString()); // for hadoop 0.21+
323 public static FileSystem getRootDirFileSystem(final Configuration c) throws IOException {
324 Path p = getRootDir(c);
325 return p.getFileSystem(c);
329 * @param c configuration
330 * @return {@link Path} to hbase log root directory: e.g. {@value HBASE_WAL_DIR} from
331 * configuration as a qualified Path. Defaults to HBase root dir.
332 * @throws IOException e
334 public static Path getWALRootDir(final Configuration c) throws IOException {
335 Path p = new Path(c.get(HBASE_WAL_DIR, c.get(HConstants.HBASE_DIR)));
336 if (!isValidWALRootDir(p, c)) {
337 return getRootDir(c);
339 FileSystem fs = p.getFileSystem(c);
340 return p.makeQualified(fs.getUri(), fs.getWorkingDirectory());
343 @VisibleForTesting
344 public static void setWALRootDir(final Configuration c, final Path root) {
345 c.set(HBASE_WAL_DIR, root.toString());
348 public static FileSystem getWALFileSystem(final Configuration c) throws IOException {
349 Path p = getWALRootDir(c);
350 FileSystem fs = p.getFileSystem(c);
351 // hadoop-core does fs caching, so need to propagate this if set
352 String enforceStreamCapability = c.get(UNSAFE_STREAM_CAPABILITY_ENFORCE);
353 if (enforceStreamCapability != null) {
354 fs.getConf().set(UNSAFE_STREAM_CAPABILITY_ENFORCE, enforceStreamCapability);
356 if (fs instanceof LocalFileSystem) {
357 // running on LocalFileSystem, which does not support the required capabilities `HSYNC`
358 // and `HFLUSH`. disable enforcement.
359 final boolean value = false;
360 LOG.warn("Cannot enforce durability guarantees while running on {}. Setting {}={} for"
361 + " this FileSystem.", fs.getUri(), UNSAFE_STREAM_CAPABILITY_ENFORCE, value);
362 fs.getConf().setBoolean(UNSAFE_STREAM_CAPABILITY_ENFORCE, value);
364 return fs;
367 private static boolean isValidWALRootDir(Path walDir, final Configuration c) throws IOException {
368 Path rootDir = getRootDir(c);
369 FileSystem fs = walDir.getFileSystem(c);
370 Path qualifiedWalDir = walDir.makeQualified(fs.getUri(), fs.getWorkingDirectory());
371 if (!qualifiedWalDir.equals(rootDir)) {
372 if (qualifiedWalDir.toString().startsWith(rootDir.toString() + "/")) {
373 throw new IllegalStateException("Illegal WAL directory specified. " +
374 "WAL directories are not permitted to be under the root directory if set.");
377 return true;
381 * Returns the WAL region directory based on the given table name and region name
382 * @param conf configuration to determine WALRootDir
383 * @param tableName Table that the region is under
384 * @param encodedRegionName Region name used for creating the final region directory
385 * @return the region directory used to store WALs under the WALRootDir
386 * @throws IOException if there is an exception determining the WALRootDir
388 public static Path getWALRegionDir(final Configuration conf, final TableName tableName,
389 final String encodedRegionName) throws IOException {
390 return new Path(getWALTableDir(conf, tableName), encodedRegionName);
394 * Returns the Table directory under the WALRootDir for the specified table name
395 * @param conf configuration used to get the WALRootDir
396 * @param tableName Table to get the directory for
397 * @return a path to the WAL table directory for the specified table
398 * @throws IOException if there is an exception determining the WALRootDir
400 public static Path getWALTableDir(final Configuration conf, final TableName tableName)
401 throws IOException {
402 Path baseDir = new Path(getWALRootDir(conf), HConstants.BASE_NAMESPACE_DIR);
403 return new Path(new Path(baseDir, tableName.getNamespaceAsString()),
404 tableName.getQualifierAsString());
408 * For backward compatibility with HBASE-20734, where we store recovered edits in a wrong
409 * directory without BASE_NAMESPACE_DIR. See HBASE-22617 for more details.
410 * @deprecated For compatibility, will be removed in 4.0.0.
412 @Deprecated
413 public static Path getWrongWALRegionDir(final Configuration conf, final TableName tableName,
414 final String encodedRegionName) throws IOException {
415 Path wrongTableDir = new Path(new Path(getWALRootDir(conf), tableName.getNamespaceAsString()),
416 tableName.getQualifierAsString());
417 return new Path(wrongTableDir, encodedRegionName);
421 * Returns the {@link org.apache.hadoop.fs.Path} object representing the table directory under
422 * path rootdir
424 * @param rootdir qualified path of HBase root directory
425 * @param tableName name of table
426 * @return {@link org.apache.hadoop.fs.Path} for table
428 public static Path getTableDir(Path rootdir, final TableName tableName) {
429 return new Path(getNamespaceDir(rootdir, tableName.getNamespaceAsString()),
430 tableName.getQualifierAsString());
434 * Returns the {@link org.apache.hadoop.hbase.TableName} object representing
435 * the table directory under
436 * path rootdir
438 * @param tablePath path of table
439 * @return {@link org.apache.hadoop.fs.Path} for table
441 public static TableName getTableName(Path tablePath) {
442 return TableName.valueOf(tablePath.getParent().getName(), tablePath.getName());
446 * Returns the {@link org.apache.hadoop.fs.Path} object representing
447 * the namespace directory under path rootdir
449 * @param rootdir qualified path of HBase root directory
450 * @param namespace namespace name
451 * @return {@link org.apache.hadoop.fs.Path} for table
453 public static Path getNamespaceDir(Path rootdir, final String namespace) {
454 return new Path(rootdir, new Path(HConstants.BASE_NAMESPACE_DIR,
455 new Path(namespace)));
458 // this mapping means that under a federated FileSystem implementation, we'll
459 // only log the first failure from any of the underlying FileSystems at WARN and all others
460 // will be at DEBUG.
461 private static final Map<FileSystem, Boolean> warningMap = new ConcurrentHashMap<>();
464 * Sets storage policy for given path.
465 * If the passed path is a directory, we'll set the storage policy for all files
466 * created in the future in said directory. Note that this change in storage
467 * policy takes place at the FileSystem level; it will persist beyond this RS's lifecycle.
468 * If we're running on a version of FileSystem that doesn't support the given storage policy
469 * (or storage policies at all), then we'll issue a log message and continue.
471 * See http://hadoop.apache.org/docs/r2.6.0/hadoop-project-dist/hadoop-hdfs/ArchivalStorage.html
473 * @param fs We only do anything it implements a setStoragePolicy method
474 * @param path the Path whose storage policy is to be set
475 * @param storagePolicy Policy to set on <code>path</code>; see hadoop 2.6+
476 * org.apache.hadoop.hdfs.protocol.HdfsConstants for possible list e.g
477 * 'COLD', 'WARM', 'HOT', 'ONE_SSD', 'ALL_SSD', 'LAZY_PERSIST'.
479 public static void setStoragePolicy(final FileSystem fs, final Path path,
480 final String storagePolicy) {
481 try {
482 setStoragePolicy(fs, path, storagePolicy, false);
483 } catch (IOException e) {
484 // should never arrive here
485 LOG.warn("We have chosen not to throw exception but some unexpectedly thrown out", e);
489 static void setStoragePolicy(final FileSystem fs, final Path path, final String storagePolicy,
490 boolean throwException) throws IOException {
491 if (storagePolicy == null) {
492 if (LOG.isTraceEnabled()) {
493 LOG.trace("We were passed a null storagePolicy, exiting early.");
495 return;
497 String trimmedStoragePolicy = storagePolicy.trim();
498 if (trimmedStoragePolicy.isEmpty()) {
499 LOG.trace("We were passed an empty storagePolicy, exiting early.");
500 return;
501 } else {
502 trimmedStoragePolicy = trimmedStoragePolicy.toUpperCase(Locale.ROOT);
504 if (trimmedStoragePolicy.equals(HConstants.DEFER_TO_HDFS_STORAGE_POLICY)) {
505 LOG.trace("We were passed the defer-to-hdfs policy {}, exiting early.", trimmedStoragePolicy);
506 return;
508 try {
509 invokeSetStoragePolicy(fs, path, trimmedStoragePolicy);
510 } catch (IOException e) {
511 LOG.trace("Failed to invoke set storage policy API on FS", e);
512 if (throwException) {
513 throw e;
519 * All args have been checked and are good. Run the setStoragePolicy invocation.
521 private static void invokeSetStoragePolicy(final FileSystem fs, final Path path,
522 final String storagePolicy) throws IOException {
523 Exception toThrow = null;
525 try {
526 fs.setStoragePolicy(path, storagePolicy);
527 LOG.debug("Set storagePolicy={} for path={}", storagePolicy, path);
528 } catch (Exception e) {
529 toThrow = e;
530 // This swallows FNFE, should we be throwing it? seems more likely to indicate dev
531 // misuse than a runtime problem with HDFS.
532 if (!warningMap.containsKey(fs)) {
533 warningMap.put(fs, true);
534 LOG.warn("Unable to set storagePolicy=" + storagePolicy + " for path=" + path + ". " +
535 "DEBUG log level might have more details.", e);
536 } else if (LOG.isDebugEnabled()) {
537 LOG.debug("Unable to set storagePolicy=" + storagePolicy + " for path=" + path, e);
540 // Hadoop 2.8+, 3.0-a1+ added FileSystem.setStoragePolicy with a default implementation
541 // that throws UnsupportedOperationException
542 if (e instanceof UnsupportedOperationException) {
543 if (LOG.isDebugEnabled()) {
544 LOG.debug("The underlying FileSystem implementation doesn't support " +
545 "setStoragePolicy. This is probably intentional on their part, since HDFS-9345 " +
546 "appears to be present in your version of Hadoop. For more information check " +
547 "the Hadoop documentation on 'ArchivalStorage', the Hadoop FileSystem " +
548 "specification docs from HADOOP-11981, and/or related documentation from the " +
549 "provider of the underlying FileSystem (its name should appear in the " +
550 "stacktrace that accompanies this message). Note in particular that Hadoop's " +
551 "local filesystem implementation doesn't support storage policies.", e);
556 if (toThrow != null) {
557 throw new IOException(toThrow);
562 * @param conf must not be null
563 * @return True if this filesystem whose scheme is 'hdfs'.
564 * @throws IOException from underlying FileSystem
566 public static boolean isHDFS(final Configuration conf) throws IOException {
567 FileSystem fs = FileSystem.get(conf);
568 String scheme = fs.getUri().getScheme();
569 return scheme.equalsIgnoreCase("hdfs");
573 * Checks if the given path is the one with 'recovered.edits' dir.
574 * @param path must not be null
575 * @return True if we recovered edits
577 public static boolean isRecoveredEdits(Path path) {
578 return path.toString().contains(HConstants.RECOVERED_EDITS_DIR);
582 * @param conf must not be null
583 * @return Returns the filesystem of the hbase rootdir.
584 * @throws IOException from underlying FileSystem
586 public static FileSystem getCurrentFileSystem(Configuration conf) throws IOException {
587 return getRootDir(conf).getFileSystem(conf);
591 * Calls fs.listStatus() and treats FileNotFoundException as non-fatal
592 * This accommodates differences between hadoop versions, where hadoop 1
593 * does not throw a FileNotFoundException, and return an empty FileStatus[]
594 * while Hadoop 2 will throw FileNotFoundException.
596 * Where possible, prefer FSUtils#listStatusWithStatusFilter(FileSystem,
597 * Path, FileStatusFilter) instead.
599 * @param fs file system
600 * @param dir directory
601 * @param filter path filter
602 * @return null if dir is empty or doesn't exist, otherwise FileStatus array
604 public static FileStatus[] listStatus(final FileSystem fs,
605 final Path dir, final PathFilter filter) throws IOException {
606 FileStatus [] status = null;
607 try {
608 status = filter == null ? fs.listStatus(dir) : fs.listStatus(dir, filter);
609 } catch (FileNotFoundException fnfe) {
610 // if directory doesn't exist, return null
611 if (LOG.isTraceEnabled()) {
612 LOG.trace("{} doesn't exist", dir);
615 if (status == null || status.length < 1) {
616 return null;
618 return status;
622 * Calls fs.listStatus() and treats FileNotFoundException as non-fatal
623 * This would accommodates differences between hadoop versions
625 * @param fs file system
626 * @param dir directory
627 * @return null if dir is empty or doesn't exist, otherwise FileStatus array
629 public static FileStatus[] listStatus(final FileSystem fs, final Path dir) throws IOException {
630 return listStatus(fs, dir, null);
634 * Calls fs.listFiles() to get FileStatus and BlockLocations together for reducing rpc call
636 * @param fs file system
637 * @param dir directory
638 * @return LocatedFileStatus list
640 public static List<LocatedFileStatus> listLocatedStatus(final FileSystem fs,
641 final Path dir) throws IOException {
642 List<LocatedFileStatus> status = null;
643 try {
644 RemoteIterator<LocatedFileStatus> locatedFileStatusRemoteIterator = fs
645 .listFiles(dir, false);
646 while (locatedFileStatusRemoteIterator.hasNext()) {
647 if (status == null) {
648 status = Lists.newArrayList();
650 status.add(locatedFileStatusRemoteIterator.next());
652 } catch (FileNotFoundException fnfe) {
653 // if directory doesn't exist, return null
654 if (LOG.isTraceEnabled()) {
655 LOG.trace("{} doesn't exist", dir);
658 return status;
662 * Calls fs.delete() and returns the value returned by the fs.delete()
664 * @param fs must not be null
665 * @param path must not be null
666 * @param recursive delete tree rooted at path
667 * @return the value returned by the fs.delete()
668 * @throws IOException from underlying FileSystem
670 public static boolean delete(final FileSystem fs, final Path path, final boolean recursive)
671 throws IOException {
672 return fs.delete(path, recursive);
676 * Calls fs.exists(). Checks if the specified path exists
678 * @param fs must not be null
679 * @param path must not be null
680 * @return the value returned by fs.exists()
681 * @throws IOException from underlying FileSystem
683 public static boolean isExists(final FileSystem fs, final Path path) throws IOException {
684 return fs.exists(path);
688 * Log the current state of the filesystem from a certain root directory
689 * @param fs filesystem to investigate
690 * @param root root file/directory to start logging from
691 * @param log log to output information
692 * @throws IOException if an unexpected exception occurs
694 public static void logFileSystemState(final FileSystem fs, final Path root, Logger log)
695 throws IOException {
696 log.debug("File system contents for path {}", root);
697 logFSTree(log, fs, root, "|-");
701 * Recursive helper to log the state of the FS
703 * @see #logFileSystemState(FileSystem, Path, Logger)
705 private static void logFSTree(Logger log, final FileSystem fs, final Path root, String prefix)
706 throws IOException {
707 FileStatus[] files = listStatus(fs, root, null);
708 if (files == null) {
709 return;
712 for (FileStatus file : files) {
713 if (file.isDirectory()) {
714 log.debug(prefix + file.getPath().getName() + "/");
715 logFSTree(log, fs, file.getPath(), prefix + "---");
716 } else {
717 log.debug(prefix + file.getPath().getName());
722 public static boolean renameAndSetModifyTime(final FileSystem fs, final Path src, final Path dest)
723 throws IOException {
724 // set the modify time for TimeToLive Cleaner
725 fs.setTimes(src, EnvironmentEdgeManager.currentTime(), -1);
726 return fs.rename(src, dest);
730 * Check if short circuit read buffer size is set and if not, set it to hbase value.
731 * @param conf must not be null
733 public static void checkShortCircuitReadBufferSize(final Configuration conf) {
734 final int defaultSize = HConstants.DEFAULT_BLOCKSIZE * 2;
735 final int notSet = -1;
736 // DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_BUFFER_SIZE_KEY is only defined in h2
737 final String dfsKey = "dfs.client.read.shortcircuit.buffer.size";
738 int size = conf.getInt(dfsKey, notSet);
739 // If a size is set, return -- we will use it.
740 if (size != notSet) {
741 return;
743 // But short circuit buffer size is normally not set. Put in place the hbase wanted size.
744 int hbaseSize = conf.getInt("hbase." + dfsKey, defaultSize);
745 conf.setIfUnset(dfsKey, Integer.toString(hbaseSize));
748 private static final class DfsBuilderUtility {
749 private static final Class<?> BUILDER;
750 private static final Method REPLICATE;
752 static {
753 String builderName = "org.apache.hadoop.hdfs.DistributedFileSystem$HdfsDataOutputStreamBuilder";
754 Class<?> builderClass = null;
755 try {
756 builderClass = Class.forName(builderName);
757 } catch (ClassNotFoundException e) {
758 LOG.debug("{} not available, will not set replicate when creating output stream", builderName);
760 Method replicateMethod = null;
761 if (builderClass != null) {
762 try {
763 replicateMethod = builderClass.getMethod("replicate");
764 LOG.debug("Using builder API via reflection for DFS file creation.");
765 } catch (NoSuchMethodException e) {
766 LOG.debug("Could not find replicate method on builder; will not set replicate when" +
767 " creating output stream", e);
770 BUILDER = builderClass;
771 REPLICATE = replicateMethod;
775 * Attempt to use builder API via reflection to call the replicate method on the given builder.
777 static void replicate(FSDataOutputStreamBuilder<?, ?> builder) {
778 if (BUILDER != null && REPLICATE != null && BUILDER.isAssignableFrom(builder.getClass())) {
779 try {
780 REPLICATE.invoke(builder);
781 } catch (IllegalAccessException | InvocationTargetException e) {
782 // Should have caught this failure during initialization, so log full trace here
783 LOG.warn("Couldn't use reflection with builder API", e);
790 * Attempt to use builder API via reflection to create a file with the given parameters and
791 * replication enabled.
792 * <p/>
793 * Will not attempt to enable replication when passed an HFileSystem.
795 public static FSDataOutputStream createForWal(FileSystem fs, Path path, boolean overwrite)
796 throws IOException {
797 FSDataOutputStreamBuilder<?, ?> builder = fs.createFile(path).overwrite(overwrite);
798 DfsBuilderUtility.replicate(builder);
799 return builder.build();
803 * Attempt to use builder API via reflection to create a file with the given parameters and
804 * replication enabled.
805 * <p/>
806 * Will not attempt to enable replication when passed an HFileSystem.
808 public static FSDataOutputStream createForWal(FileSystem fs, Path path, boolean overwrite,
809 int bufferSize, short replication, long blockSize, boolean isRecursive) throws IOException {
810 FSDataOutputStreamBuilder<?, ?> builder = fs.createFile(path).overwrite(overwrite)
811 .bufferSize(bufferSize).replication(replication).blockSize(blockSize);
812 if (isRecursive) {
813 builder.recursive();
815 DfsBuilderUtility.replicate(builder);
816 return builder.build();
820 * Helper exception for those cases where the place where we need to check a stream capability
821 * is not where we have the needed context to explain the impact and mitigation for a lack.
823 public static class StreamLacksCapabilityException extends Exception {
824 public StreamLacksCapabilityException(String message, Throwable cause) {
825 super(message, cause);
827 public StreamLacksCapabilityException(String message) {
828 super(message);