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

org.cloudfoundry.util.FileUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2013-2021 the original author or authors.
 *
 * Licensed 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.cloudfoundry.util;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.PosixFilePermission;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;
import reactor.core.Exceptions;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

/**
 * Utilities for files
 */
public final class FileUtils {

    private static final Integer DEFAULT_PERMISSIONS = 0744;

    private static final Map PERMISSION_MODES =
            FluentMap.builder()
                    .entry(PosixFilePermission.OWNER_READ, 0400)
                    .entry(PosixFilePermission.OWNER_WRITE, 0200)
                    .entry(PosixFilePermission.OWNER_EXECUTE, 0100)
                    .entry(PosixFilePermission.GROUP_READ, 0040)
                    .entry(PosixFilePermission.GROUP_WRITE, 0020)
                    .entry(PosixFilePermission.GROUP_EXECUTE, 0010)
                    .entry(PosixFilePermission.OTHERS_READ, 0004)
                    .entry(PosixFilePermission.OTHERS_WRITE, 0002)
                    .entry(PosixFilePermission.OTHERS_EXECUTE, 0001)
                    .build();

    private FileUtils() {}

    /**
     * Compresses a candidate {@link Path} if it is a directory.  Otherwise returns the original {@link Path}.
     *
     * @param candidate the candidate {@link Path} to compress
     * @return the {@link Path} for a compressed artifact
     */
    public static Mono compress(Path candidate) {
        return compress(candidate, path -> true);
    }

    /**
     * Compresses a candidate {@link Path} filtering out entries
     *
     * @param candidate the candidate {@link Path} to compress
     * @param filter    a filter applied to each path
     * @return the {@link Path} for a compressed artifact
     */
    public static Mono compress(Path candidate, Predicate filter) {
        return Mono.defer(
                        () -> {
                            try {
                                Path staging =
                                        Files.createTempFile(
                                                String.format(
                                                        "compressed-%s-", candidate.getFileName()),
                                                ".zip");

                                try (ZipArchiveOutputStream out =
                                        new ZipArchiveOutputStream(staging.toFile())) {
                                    if (Files.isDirectory(candidate)) {
                                        compressFromDirectory(candidate, filter, out);
                                    } else {
                                        compressFromZip(candidate, filter, out);
                                    }
                                }

                                return Mono.just(staging);
                            } catch (IOException e) {
                                throw Exceptions.propagate(e);
                            }
                        })
                .subscribeOn(Schedulers.boundedElastic());
    }

    /**
     * Get the relative path of an application
     *
     * @param root the root to relativize against
     * @param path the path to relativize
     * @return the relative path
     */
    public static String getRelativePathName(Path root, Path path) {
        Path relative = root.relativize(path);
        return Files.isDirectory(path) && !relative.toString().endsWith("/")
                ? String.format("%s/", relative.toString())
                : relative.toString();
    }

    /**
     * Calculates the SHA-1 hash for a {@link Path}
     *
     * @param path the {@link Path} to calculate the hash for
     * @return a {@link String} representation of the hash
     */
    public static String hash(Path path) {
        try (InputStream in = Files.newInputStream(path)) {
            return hash(in);
        } catch (IOException e) {
            throw Exceptions.propagate(e);
        }
    }

    /**
     * Calculates the SHA-1 hash for an {@link InputStream}
     *
     * @param in the {@link InputStream} to calculate the hash for
     * @return {@link String} representation of the hash
     */
    public static String hash(InputStream in) {
        try {
            MessageDigest digest = MessageDigest.getInstance("sha1");

            ByteArrayPool.withByteArray(
                    buffer -> {
                        try {
                            int length;
                            while ((length = in.read(buffer)) != -1) {
                                digest.update(buffer, 0, length);
                            }
                        } catch (IOException e) {
                            throw Exceptions.propagate(e);
                        }
                    });

            return String.format("%040x", new BigInteger(1, digest.digest()));
        } catch (NoSuchAlgorithmException e) {
            throw Exceptions.propagate(e);
        }
    }

    /**
     * Calculates permissions for a {@link Path}
     *
     * @param path the {@link Path} to calculate the permissions for
     * @return a {@link String} representation of the permissions
     */
    public static String permissions(Path path) {
        try {
            return permissions(getUnixMode(path));
        } catch (IOException e) {
            throw Exceptions.propagate(e);
        }
    }

    /**
     * Calculates permissions for a UNIX mode
     *
     * @param mode the UNIX mode to calculate the permissions for
     * @return a {@link String} representation of the permissions
     */
    public static String permissions(int mode) {
        return Integer.toOctalString(
                mode == 0
                        ? DEFAULT_PERMISSIONS
                        : mode & 07777 // only use permission bits, not the file type
                );
    }

    /**
     * Calculates the size of a {@link Path}
     *
     * @param path the {@link Path} to calculate the size for
     * @return the size
     */
    public static int size(Path path) {
        try {
            return (int) Files.size(path);
        } catch (IOException e) {
            throw Exceptions.propagate(e);
        }
    }

    private static void compressFromDirectory(
            Path candidate, Predicate filter, ZipArchiveOutputStream out) {
        try (Stream contents = Files.walk(candidate)) {
            contents.filter(
                            path -> {
                                try {
                                    return !Files.isSameFile(candidate, path);
                                } catch (IOException e) {
                                    throw Exceptions.propagate(e);
                                }
                            })
                    .filter(path -> filter.test(getRelativePathName(candidate, path)))
                    .forEach(
                            path -> {
                                try (InputStream in =
                                        Files.isDirectory(path)
                                                ? null
                                                : Files.newInputStream(path)) {
                                    write(
                                            in,
                                            Files.getLastModifiedTime(path),
                                            getUnixMode(path),
                                            out,
                                            getRelativePathName(candidate, path));
                                } catch (IOException e) {
                                    throw Exceptions.propagate(e);
                                }
                            });
        } catch (IOException e) {
            throw Exceptions.propagate(e);
        }
    }

    private static void compressFromZip(
            Path candidate, Predicate filter, ZipArchiveOutputStream out) {
        try (ZipFile zipFile = new ZipFile(candidate.toFile())) {
            Enumeration entries = zipFile.getEntries();

            while (entries.hasMoreElements()) {
                ZipArchiveEntry entry = entries.nextElement();

                if (filter.test(entry.getName())) {
                    try (InputStream in = zipFile.getInputStream(entry)) {
                        int mode = entry.getUnixMode();
                        write(
                                in,
                                entry.getLastModifiedTime(),
                                mode == 0 ? DEFAULT_PERMISSIONS : mode,
                                out,
                                entry.getName());
                    }
                }
            }
        } catch (IOException e) {
            throw Exceptions.propagate(e);
        }
    }

    private static int getUnixMode(Path path) throws IOException {
        if (!isPosixFile(path)) {
            return DEFAULT_PERMISSIONS;
        }

        return Files.getPosixFilePermissions(path).stream().mapToInt(PERMISSION_MODES::get).sum();
    }

    private static boolean isPosixFile(Path path) {
        return path.getFileSystem().supportedFileAttributeViews().contains("posix");
    }

    private static void write(
            InputStream in,
            FileTime lastModifiedTime,
            int mode,
            ZipArchiveOutputStream out,
            String path) {
        try {
            ZipArchiveEntry entry = new ZipArchiveEntry(path);
            entry.setUnixMode(mode);
            entry.setLastModifiedTime(lastModifiedTime);
            out.putArchiveEntry(entry);

            if (in != null) {
                ByteArrayPool.withByteArray(
                        buffer -> {
                            try {
                                int length;
                                while ((length = in.read(buffer)) != -1) {
                                    out.write(buffer, 0, length);
                                }
                            } catch (IOException e) {
                                throw Exceptions.propagate(e);
                            }
                        });
            }

            out.closeArchiveEntry();
        } catch (IOException e) {
            throw Exceptions.propagate(e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy