tachyon.util.io.FileUtils.java Source code

Java tutorial

Introduction

Here is the source code for tachyon.util.io.FileUtils.java

Source

/*
 * Licensed to the University of California, Berkeley under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package tachyon.util.io;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import com.google.common.io.Files;

import tachyon.Constants;
import tachyon.TachyonURI;
import tachyon.exception.InvalidPathException;

/**
 * Provides utility methods for working with files and directories.
 *
 * By convention, methods take file path strings as parameters.
 */
public final class FileUtils {
    private static final Logger LOG = LoggerFactory.getLogger(Constants.LOGGER_TYPE);

    /**
     * Changes local file's permission.
     *
     * @param filePath that will change permission
     * @param perms the permission, e.g. "775"
     * @throws IOException when fails to change permission
     */
    public static void changeLocalFilePermission(String filePath, String perms) throws IOException {
        // TODO(cc): Switch to java's Files.setPosixFilePermissions() when Java 6 support is dropped.
        List<String> commands = new ArrayList<String>();
        commands.add("/bin/chmod");
        commands.add(perms);
        File file = new File(filePath);
        commands.add(file.getAbsolutePath());

        try {
            ProcessBuilder builder = new ProcessBuilder(commands);
            Process process = builder.start();

            process.waitFor();

            redirectIO(process);

            if (process.exitValue() != 0) {
                throw new IOException(
                        "Can not change the file " + file.getAbsolutePath() + " 's permission to be " + perms);
            }
        } catch (InterruptedException e) {
            LOG.error(e.getMessage());
            throw new IOException(e);
        }
    }

    /**
     * Blocking operation that copies the processes stdout/stderr to this JVM's stdout/stderr.
     *
     * @param process process whose stdout/stderr to copy
     * @throws IOException when operation fails
     */
    private static void redirectIO(final Process process) throws IOException {
        Preconditions.checkNotNull(process);
        /*
         * Because chmod doesn't have a lot of error or output messages, it is safe to process the
         * output after the process is done. As of java 7, you can have the process redirect to
         * System.out and System.err without forking a process.
         *
         * TODO(cc): When java 6 support is dropped switch to ProcessBuilder.html#inheritIO().
         */
        Closer closer = Closer.create();
        try {
            ByteStreams.copy(closer.register(process.getInputStream()), System.out);
            ByteStreams.copy(closer.register(process.getErrorStream()), System.err);
        } catch (Exception e) {
            throw closer.rethrow(e);
        } finally {
            closer.close();
        }
    }

    /**
     * Changes local file's permission to be 777.
     *
     * @param filePath that will change permission
     * @throws IOException when fails to change file's permission to 777
     */
    public static void changeLocalFileToFullPermission(String filePath) throws IOException {
        changeLocalFilePermission(filePath, "777");
    }

    /**
     * Sticky bit can be set primarily on directories in UNIX / Linux.
     *
     * If the sticky bit of is enabled on a directory, only the owner and the root user can delete /
     * rename the files or directories within that directory. No one else can delete other users data
     * in this directory(Where sticky bit is set).
     *
     * This is a security measure to avoid deletion of folders and their content (sub-folders and
     * files), though other users have full permissions.
     *
     * Setting the sticky bit of a file is a no-op.
     *
     * @param dir absolute dir path to set the sticky bit
     */
    public static void setLocalDirStickyBit(String dir) {
        try {
            // Support for sticky bit is platform specific. Check if the path starts with "/" and if so,
            // assume that the host supports the chmod command.
            if (dir.startsWith(TachyonURI.SEPARATOR)) {
                Runtime.getRuntime().exec("chmod +t " + dir);
            }
        } catch (IOException e) {
            LOG.info("Can not set the sticky bit of the directory: {}", dir);
        }
    }

    /**
     * Creates the local block path and all the parent directories. Also, sets the appropriate
     * permissions.
     *
     * @param path the path of the block
     * @throws IOException when fails to create block path and parent directories with appropriate
     *         permissions.
     */
    public static void createBlockPath(String path) throws IOException {
        try {
            createStorageDirPath(PathUtils.getParent(path));
        } catch (InvalidPathException e) {
            throw new IOException("Failed to create block path, get parent path of " + path + "failed");
        } catch (IOException ioe) {
            throw new IOException("Failed to create block path " + path);
        }
    }

    /**
     * Moves file from one place to another, can across storage devices (e.g., from memory to SSD)
     * when {@link File#renameTo} may not work.
     *
     * Current implementation uses {@link com.google.common.io.Files#move(File, File)}, may change if
     * there is a better solution.
     *
     * @param srcPath pathname string of source file
     * @param dstPath pathname string of destination file
     * @throws IOException when fails to move
     */
    public static void move(String srcPath, String dstPath) throws IOException {
        Files.move(new File(srcPath), new File(dstPath));
    }

    /**
     * Deletes the file or directory.
     *
     * Current implementation uses {@link java.io.File#delete()}, may change if there is a better
     * solution.
     *
     * @param path pathname string of file or directory
     * @throws IOException when fails to delete
     */
    public static void delete(String path) throws IOException {
        File file = new File(path);
        if (!file.delete()) {
            throw new IOException("Failed to delete " + path);
        }
    }

    /**
     * Creates the storage directory path, including any necessary but nonexistent parent directories.
     * If the directory already exists, do nothing.
     *
     * Also, appropriate directory permissions (777 + StickyBit, namely "drwxrwxrwt") are set.
     *
     * @param path storage directory path to create
     * @throws IOException when fails to create storage directory path
     */
    public static void createStorageDirPath(String path) throws IOException {
        File dir = new File(path);
        String absolutePath = dir.getAbsolutePath();
        if (!dir.exists()) {
            if (dir.mkdirs()) {
                changeLocalFileToFullPermission(absolutePath);
                setLocalDirStickyBit(absolutePath);
                LOG.info("Folder {} was created!", path);
            } else {
                throw new IOException("Failed to create folder " + path);
            }
        }
    }

    /**
     * Creates an empty file and its intermediate directories if necessary.
     *
     * @param filePath pathname string of the file to create
     * @throws IOException if an I/O error occurred or file already exists
     */
    public static void createFile(String filePath) throws IOException {
        File file = new File(filePath);
        Files.createParentDirs(file);
        if (!file.createNewFile()) {
            throw new IOException("File already exists " + filePath);
        }
    }

    /**
     * Creates an empty directory and its intermediate directories if necessary.
     *
     * @param path path of the directory to create
     * @throws IOException if an I/O error occurred or directory already exists
     */
    public static void createDir(String path) throws IOException {
        new File(path).mkdirs();
    }

    /**
     * Checks if a path exists.
     *
     * @param path the given path
     * @return true if path exists, false otherwise
     */
    public static boolean exists(String path) {
        return new File(path).exists();
    }

    private FileUtils() {
    } // prevent instantiation
}