/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.fabric.netty.fileserver;
import com.liferay.portal.kernel.io.BigEndianCodec;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.ReflectionUtil;
import com.liferay.portal.kernel.util.StringBundler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* @author Shuyang Zhou
*/
public class FileHelperUtil {
public static final Path TEMP_DIR_PATH = Paths.get(
System.getProperty("java.io.tmpdir"));
public static void delete(final boolean quiet, Path... paths) {
try {
for (Path path : paths) {
Files.walkFileTree(
path,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(
Path dir, IOException ioe)
throws IOException {
if ((ioe != null) && !quiet) {
throw ioe;
}
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(
Path file,
BasicFileAttributes basicFileAttributes)
throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(
Path file, IOException ioe)
throws IOException {
if (quiet || (ioe instanceof NoSuchFileException)) {
return FileVisitResult.CONTINUE;
}
throw ioe;
}
});
}
}
catch (IOException ioe) {
if (!quiet) {
ReflectionUtil.throwException(ioe);
}
}
}
public static void delete(Path... paths) {
delete(false, paths);
}
public static void move(Path fromPath, final Path toPath)
throws IOException {
move(fromPath, toPath, true);
}
public static void move(
final Path fromPath, final Path toPath, boolean tryAtomicMove)
throws IOException {
final AtomicBoolean atomicMove = new AtomicBoolean(tryAtomicMove);
final AtomicBoolean touched = new AtomicBoolean();
final Map<Path, FileTime> fileTimes = new HashMap<>();
try {
Files.walkFileTree(
fromPath,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(
Path dir, IOException ioe)
throws IOException {
Files.setLastModifiedTime(
toPath.resolve(fromPath.relativize(dir)),
fileTimes.remove(dir));
if (atomicMove.get()) {
Files.delete(dir);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(
Path dir, BasicFileAttributes basicFileAttributes)
throws IOException {
Files.copy(
dir, toPath.resolve(fromPath.relativize(dir)),
StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.REPLACE_EXISTING);
fileTimes.put(dir, Files.getLastModifiedTime(dir));
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(
Path file, BasicFileAttributes basicFileAttributes)
throws IOException {
Path toFile = toPath.resolve(fromPath.relativize(file));
if (atomicMove.get()) {
try {
Files.move(
file, toFile,
StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING);
touched.set(true);
return FileVisitResult.CONTINUE;
}
catch (AtomicMoveNotSupportedException amnse) {
atomicMove.set(false);
}
}
Files.copy(
file, toFile, StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
});
}
catch (IOException ioe) {
delete(true, toPath);
if (touched.get()) {
throw new IOException(
"Source path " + fromPath + " was left in an " +
"inconsistent state",
ioe);
}
throw ioe;
}
if (!atomicMove.get()) {
delete(false, fromPath);
}
}
public static Path unzip(Path sourcePath, Path destDirPath)
throws IOException {
Path destPath = Files.createTempDirectory(destDirPath, null);
try (InputStream inputStream = Files.newInputStream(sourcePath)) {
long startTime = System.currentTimeMillis();
long rawSize = unzip(new ZipInputStream(inputStream), destPath);
if (_log.isDebugEnabled()) {
long zippedSize = Files.size(sourcePath);
long time = (System.currentTimeMillis() - startTime) / 1000;
double compressionRatio = rawSize / zippedSize;
StringBundler sb = new StringBundler(13);
sb.append("Unzipped ");
sb.append(sourcePath);
sb.append(" (");
sb.append(zippedSize);
sb.append(" bytes) to ");
sb.append(destPath);
sb.append(" (");
sb.append(rawSize);
sb.append(" bytes)\" in ");
sb.append(time);
sb.append("s with a ");
sb.append(compressionRatio);
sb.append("compression ratio");
_log.debug(sb.toString());
}
}
catch (IOException ioe) {
delete(destPath);
throw ioe;
}
return destPath;
}
public static long unzip(ZipInputStream zipInputStream, Path destPath)
throws IOException {
final AtomicLong rawSize = new AtomicLong();
try (ZipInputStream autoCloseZipInputStream = zipInputStream) {
ZipEntry zipEntry = null;
while ((zipEntry = autoCloseZipInputStream.getNextEntry()) !=
null) {
if (zipEntry.isDirectory()) {
continue;
}
Path entryPath = destPath.resolve(zipEntry.getName());
Files.createDirectories(entryPath.getParent());
long size = Files.copy(autoCloseZipInputStream, entryPath);
rawSize.addAndGet(size);
Files.setLastModifiedTime(
entryPath,
FileTime.fromMillis(
BigEndianCodec.getLong(zipEntry.getExtra(), 0)));
long length = BigEndianCodec.getLong(zipEntry.getExtra(), 8);
if (size != length) {
throw new IOException(
"Zip stream for entry " + zipEntry.getName() + " is " +
size + " bytes but should " + length + " bytes");
}
}
}
return rawSize.get();
}
public static Path zip(
Path sourcePath, Path destDirPath,
CompressionLevel compressionLevel)
throws IOException {
Path zipPath = Files.createTempFile(destDirPath, null, null);
try (OutputStream outputStream = Files.newOutputStream(zipPath)) {
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
zipOutputStream.setLevel(compressionLevel.getLevel());
long startTime = System.currentTimeMillis();
long rawSize = zip(sourcePath, zipOutputStream);
if (_log.isDebugEnabled()) {
long zippedSize = Files.size(zipPath);
long time = (System.currentTimeMillis() - startTime) / 1000;
double compressionRatio = rawSize / zippedSize;
StringBundler sb = new StringBundler(13);
sb.append("Zipped ");
sb.append(sourcePath);
sb.append(" (");
sb.append(rawSize);
sb.append(" bytes) to ");
sb.append(zipPath);
sb.append(" (");
sb.append(zippedSize);
sb.append(" bytes)\" in ");
sb.append(time);
sb.append("s with a ");
sb.append(compressionRatio);
sb.append("compression ratio");
_log.debug(sb.toString());
}
}
catch (IOException ioe) {
Files.delete(zipPath);
throw ioe;
}
return zipPath;
}
public static long zip(
final Path sourcePath, ZipOutputStream zipOutputStream)
throws IOException {
final AtomicLong rawSize = new AtomicLong();
final byte[] buffer = new byte[16];
try (ZipOutputStream autoClosezipOutputStream = zipOutputStream) {
Files.walkFileTree(
sourcePath,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(
Path file, BasicFileAttributes basicFileAttributes)
throws IOException {
Path relativePath = sourcePath.relativize(file);
ZipEntry zipEntry = new ZipEntry(
relativePath.toString());
FileTime fileTime =
basicFileAttributes.lastModifiedTime();
rawSize.addAndGet(basicFileAttributes.size());
BigEndianCodec.putLong(buffer, 0, fileTime.toMillis());
BigEndianCodec.putLong(
buffer, 8, basicFileAttributes.size());
zipEntry.setExtra(buffer);
autoClosezipOutputStream.putNextEntry(zipEntry);
Files.copy(file, autoClosezipOutputStream);
autoClosezipOutputStream.closeEntry();
return FileVisitResult.CONTINUE;
}
});
}
return rawSize.get();
}
private static final Log _log = LogFactoryUtil.getLog(FileHelperUtil.class);
}