All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.geode.internal.FileUtil Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) 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 org.apache.geode.internal;

import org.apache.geode.distributed.internal.DistributionConfig;

import java.io.*;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

/**
 * This class contains static methods for manipulating files and directories, such as recursively
 * copying or deleting files.
 * 
 * TODO A lot of this functionality is probably duplicating apache commons io, maybe we should
 * switch to that.
 * 
 * 
 */
public class FileUtil {
  public static final long MAX_TRANSFER_SIZE =
      Long.getLong(DistributionConfig.GEMFIRE_PREFIX + "FileUtil.MAX_TRANSFER_SIZE", 1024 * 1024)
          .longValue();
  public static final boolean USE_NIO =
      !Boolean.getBoolean(DistributionConfig.GEMFIRE_PREFIX + "FileUtil.USE_OLD_IO");
  public static final String extSeparator = ".";

  /**
   * Copy a file from the source file to the destination file. If the source is a directory, it will
   * be copied recursively.
   * 
   * Note that unlike unix cp, if the destination is directory, the source *contents* will be copied
   * to the destination *contents*, not as a subdirectory of dest.
   * 
   * @param source the source file or directory
   * @param dest the destination file or directory.
   * @throws IOException
   */
  public static void copy(File source, File dest) throws IOException {
    if (source.isDirectory()) {
      dest.mkdir();
      for (File child : listFiles(source)) {
        copy(child, new File(dest, child.getName()));
      }
    } else {
      if (source.exists()) {
        long lm = source.lastModified();
        if (dest.isDirectory()) {
          dest = new File(dest, source.getName());
        }
        FileOutputStream fos = new FileOutputStream(dest);
        try {
          FileInputStream fis = new FileInputStream(source);
          try {
            if (USE_NIO) {
              nioCopy(fos, fis);
            } else {
              oioCopy(source, fos, fis);
            }
          } finally {
            fis.close();
          }
        } finally {
          fos.close();
        }
        dest.setExecutable(source.canExecute(), true);
        dest.setLastModified(lm);
      }
    }
  }

  /**
   * Basically just like {@link File#listFiles()} but instead of returning null returns an empty
   * array. This fixes bug 43729
   */
  public static File[] listFiles(File dir) {
    File[] result = dir.listFiles();
    if (result == null) {
      result = new File[0];
    }
    return result;
  }

  /**
   * Basically just like {@link File#listFiles(FilenameFilter)} but instead of returning null
   * returns an empty array. This fixes bug 43729
   */
  public static File[] listFiles(File dir, FilenameFilter filter) {
    File[] result = dir.listFiles(filter);
    if (result == null) {
      result = new File[0];
    }
    return result;
  }

  /**
   * Copy a single file using NIO.
   * 
   * @throws IOException
   */
  private static void nioCopy(FileOutputStream fos, FileInputStream fis) throws IOException {
    FileChannel outChannel = fos.getChannel();
    FileChannel inChannel = fis.getChannel();
    long length = inChannel.size();
    long offset = 0;
    while (true) {
      long remaining = length - offset;

      long toTransfer = remaining < MAX_TRANSFER_SIZE ? remaining : MAX_TRANSFER_SIZE;
      long transferredBytes = inChannel.transferTo(offset, toTransfer, outChannel);
      offset += transferredBytes;
      length = inChannel.size();
      if (offset >= length) {
        break;
      }
    }
  }

  /**
   * Copy a single file using the java.io.
   * 
   * @throws IOException
   */
  private static void oioCopy(File source, FileOutputStream fos, FileInputStream fis)
      throws IOException {
    int size = (int) (source.length() < MAX_TRANSFER_SIZE ? source.length() : MAX_TRANSFER_SIZE);
    byte[] buffer = new byte[size];
    int read;
    while ((read = fis.read(buffer)) > 0) {
      fos.write(buffer, 0, read);
    }

  }

  /**
   * Recursively delete a file or directory.
   * 
   * @throws IOException if the file or directory couldn't be deleted. Unlike File.delete, which
   *         just returns false.
   */
  public static void delete(File file) throws IOException {
    if (!file.exists())
      return;

    if (file.isDirectory()) {
      for (File child : listFiles(file)) {
        delete(child);
      }
    }

    Files.delete(file.toPath());
  }

  /**
   * Recursively delete a file or directory. A description of any files or directories that can not
   * be deleted will be added to failures if failures is non-null. This method tries to delete as
   * much as possible.
   */
  public static void delete(File file, StringBuilder failures) {
    if (!file.exists())
      return;

    if (file.isDirectory()) {
      for (File child : listFiles(file)) {
        delete(child, failures);
      }
    }

    try {
      Files.delete(file.toPath());
    } catch (IOException e) {
      if (failures != null) {
        failures.append("Could not delete ").append(file).append(" due to ").append(e.getMessage())
            .append('\n');
      }
    }
  }

  /**
   * Find the file whose name matches the given regular expression. The regex is matched against the
   * absolute path of the file.
   * 
   * This could probably use a lot of optimization!
   */
  public static File find(File baseFile, String regex) {
    if (baseFile.getAbsolutePath().matches(regex)) {
      return baseFile;
    }
    if (baseFile.exists() && baseFile.isDirectory()) {
      for (File child : listFiles(baseFile)) {
        File foundFile = find(child, regex);
        if (foundFile != null) {
          return foundFile;
        }
      }
    }
    return null;
  }

  /**
   * Find a files in a given base directory that match a the given regex. The regex is matched
   * against the full path of the file.
   */
  public static List findAll(File baseFile, String regex) {
    ArrayList found = new ArrayList();
    findAll(baseFile, regex, found);
    return found;
  }

  /**
   * Destroys all files that match the given regex that are in the given directory. If a destroy
   * fails it is ignored and an attempt is made to destroy any other files that match.
   */
  public static void deleteMatching(File baseFile, String regex) {
    if (baseFile.exists() && baseFile.isDirectory()) {
      for (File child : listFiles(baseFile)) {
        if (child.getName().matches(regex)) {
          try {
            delete(child);
          } catch (IOException ignore) {
          }
        }
      }
    }
  }

  /** Implementation of findAll. */
  private static void findAll(File baseFile, String regex, List found) {
    if (baseFile.getAbsolutePath().matches(regex)) {
      found.add(baseFile);
    }
    if (baseFile.exists() && baseFile.isDirectory()) {
      for (File child : listFiles(baseFile)) {
        findAll(child, regex, found);
      }
    }
  }

  /**
   * Convert a file into a relative path from a given parent. This is useful if you want to write
   * out the file name into that parent directory.
   * 
   * @param parent The parent directory.
   * @param file The file we want to covert to a relative file.
   * @return A file, such that new File(parent, returnValue) == file. Note that if file does not
   *         have the parent in it's path, an the absolute version if the file is returned.
   */
  public static File removeParent(File parent, File file) {
    String absolutePath = file.getAbsolutePath();
    String parentAbsolutePath = parent.getAbsolutePath();
    String newPath = absolutePath.replace(parentAbsolutePath + "/", "");
    return new File(newPath);
  }

  /**
   * Copy a URL to a file.
   * 
   * @throws IOException
   */
  public static void copy(URL url, File file) throws IOException {
    InputStream is = url.openStream();
    try {
      OutputStream os = new FileOutputStream(file);
      try {
        byte[] buffer = new byte[8192];
        int read;
        while ((read = is.read(buffer)) > 0) {
          os.write(buffer, 0, read);
        }
      } finally {
        os.close();
      }
    } finally {
      is.close();
    }

  }

  /**
   * A safer version of File.mkdirs, which works around a race in the 1.5 JDK where two VMs creating
   * the same directory chain at the same time could end up in one VM failing to create a
   * subdirectory.
   * 
   * @param file
   */
  public static boolean mkdirs(File file) {
    final File parentFile = file.getAbsoluteFile().getParentFile();
    if (!parentFile.exists()) {
      mkdirs(parentFile);
    }
    // As long as someone successfully created the parent file
    // go ahead and create the child directory.
    if (parentFile.exists()) {
      return file.mkdir();
    } else {
      return false;
    }
  }

  /**
   * Returns the file name with the extension stripped off (if it has one).
   * 
   * @param fileName the file name
   * @return the file name with the extension stripped off (if it had one)
   */
  public static String stripOffExtension(final String fileName) {
    if (fileName.contains(extSeparator)) {
      // strip off the extension and right-most "."
      return fileName.substring(0, fileName.lastIndexOf(extSeparator));
    }
    return fileName;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy