/**
* 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.tools.bundle.support.internal.util;
import com.liferay.portal.tools.bundle.support.BundleSupport;
import com.liferay.portal.tools.bundle.support.util.StreamLogger;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
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.security.CodeSource;
import java.security.ProtectionDomain;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.DateUtils;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.RedirectLocations;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
/**
* @author Andrea Di Giorgi
* @author David Truong
*/
public class FileUtil {
public static void appendTar(File entryFile, Path entryPath, File tarFile)
throws IOException {
try (TarArchiveOutputStream tarArchiveOutputStream =
new TarArchiveOutputStream(
new GzipCompressorOutputStream(
new BufferedOutputStream(
new FileOutputStream(tarFile))))) {
_appendTar(entryFile, entryPath, tarArchiveOutputStream);
}
}
public static void appendZip(File entryFile, Path entryPath, File zipFile)
throws Exception {
try (FileSystem fileSystem = _createFileSystem(
zipFile.toPath(), false)) {
_appendZip(entryFile, entryPath, fileSystem);
}
}
public static void copyDirectory(
final Path dirPath, final Path destinationDirPath)
throws IOException {
Files.walkFileTree(
dirPath,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(
Path path, BasicFileAttributes basicFileAttributes)
throws IOException {
Path relativePath = dirPath.relativize(path);
Path destinationPath = destinationDirPath.resolve(
relativePath);
copyFile(path, destinationPath);
return FileVisitResult.CONTINUE;
}
});
}
public static void copyFile(Path path, Path destinationPath)
throws IOException {
Files.createDirectories(destinationPath.getParent());
Files.copy(path, destinationPath, StandardCopyOption.REPLACE_EXISTING);
Files.setLastModifiedTime(
destinationPath, Files.getLastModifiedTime(path));
}
public static void deleteDirectory(Path dirPath) throws IOException {
if (Files.notExists(dirPath)) {
return;
}
Files.walkFileTree(
dirPath,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(
Path path, IOException ioe)
throws IOException {
Files.delete(path);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(
Path path, BasicFileAttributes basicFileAttributes)
throws IOException {
Files.delete(path);
return FileVisitResult.CONTINUE;
}
});
}
public static Path downloadFile(
URI uri, String userName, String password, Path cacheDirPath,
StreamLogger streamLogger)
throws Exception {
Path path;
try (CloseableHttpClient closeableHttpClient = _getHttpClient(
uri, userName, password)) {
HttpHead httpHead = new HttpHead(uri);
HttpContext httpContext = new BasicHttpContext();
String fileName = null;
Date lastModifiedDate;
try (CloseableHttpResponse closeableHttpResponse =
closeableHttpClient.execute(httpHead, httpContext)) {
_checkResponseStatus(closeableHttpResponse);
Header dispositionHeader = closeableHttpResponse.getFirstHeader(
"Content-Disposition");
if (dispositionHeader != null) {
String dispositionValue = dispositionHeader.getValue();
int index = dispositionValue.indexOf("filename=");
if (index > 0) {
fileName = dispositionValue.substring(
index + 10, dispositionValue.length() - 1);
}
}
else {
RedirectLocations redirectLocations = (RedirectLocations)
httpContext.getAttribute(
HttpClientContext.REDIRECT_LOCATIONS);
if (redirectLocations != null) {
uri = redirectLocations.get(
redirectLocations.size() - 1);
}
}
Header lastModifiedHeader =
closeableHttpResponse.getFirstHeader(
HttpHeaders.LAST_MODIFIED);
if (lastModifiedHeader != null) {
lastModifiedDate = DateUtils.parseDate(
lastModifiedHeader.getValue());
}
else {
lastModifiedDate = new Date();
}
}
if (fileName == null) {
String uriPath = uri.getPath();
fileName = uriPath.substring(uriPath.lastIndexOf('/') + 1);
}
if (cacheDirPath == null) {
cacheDirPath = Files.createTempDirectory(null);
}
path = cacheDirPath.resolve(fileName);
if (Files.exists(path)) {
FileTime fileTime = Files.getLastModifiedTime(path);
if (fileTime.toMillis() == lastModifiedDate.getTime()) {
return path;
}
Files.delete(path);
}
Files.createDirectories(cacheDirPath);
HttpGet httpGet = new HttpGet(uri);
try (CloseableHttpResponse closeableHttpResponse =
closeableHttpClient.execute(httpGet)) {
_checkResponseStatus(closeableHttpResponse);
HttpEntity httpEntity = closeableHttpResponse.getEntity();
long length = httpEntity.getContentLength();
streamLogger.onStarted();
try (InputStream inputStream = httpEntity.getContent();
OutputStream outputStream = Files.newOutputStream(path)) {
byte[] buffer = new byte[10 * 1024];
int completed = 0;
int read = -1;
while ((read = inputStream.read(buffer)) >= 0) {
outputStream.write(buffer, 0, read);
completed += read;
streamLogger.onProgress(completed, length);
}
}
finally {
streamLogger.onCompleted();
}
}
Files.setLastModifiedTime(
path, FileTime.fromMillis(lastModifiedDate.getTime()));
}
return path;
}
public static String getFileLength(long length) {
DecimalFormat decimalFormat = new DecimalFormat("#.##");
if (length > _FILE_LENGTH_MB) {
return decimalFormat.format((double)length / _FILE_LENGTH_MB) +
" MB";
}
if (length > +_FILE_LENGTH_KB) {
return decimalFormat.format((double)length / _FILE_LENGTH_KB) +
" KB";
}
return decimalFormat.format(length) + " B";
}
public static File getJarFile() throws Exception {
ProtectionDomain protectionDomain =
BundleSupport.class.getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
URL url = codeSource.getLocation();
return new File(url.toURI());
}
public static boolean isPosixSupported(Path path) {
FileSystem fileSystem = path.getFileSystem();
Set<String> supportedFileAttributeViews =
fileSystem.supportedFileAttributeViews();
return supportedFileAttributeViews.contains("posix");
}
public static void tar(Path sourcePath, File tarFile, boolean includeFolder)
throws Exception {
final Path parentPath;
if (includeFolder) {
parentPath = sourcePath.getParent();
}
else {
parentPath = sourcePath;
}
try (TarArchiveOutputStream tarArchiveOutputStream =
new TarArchiveOutputStream(
new GzipCompressorOutputStream(
new BufferedOutputStream(
new FileOutputStream(tarFile))))) {
Files.walkFileTree(
sourcePath,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(
Path path, BasicFileAttributes basicFileAttributes)
throws IOException {
Path entryPath = parentPath.relativize(path);
_appendTar(
path.toFile(), entryPath, tarArchiveOutputStream);
return FileVisitResult.CONTINUE;
}
});
}
}
public static void unpack(
Path path, Path destinationDirPath, int stripComponents)
throws Exception {
String fileName = String.valueOf(path.getFileName());
if (fileName.endsWith(".gz") || fileName.endsWith(".tar") ||
fileName.endsWith(".tgz")) {
_untar(path, destinationDirPath, stripComponents);
}
else if (fileName.endsWith(".zip")) {
_unzip(path, destinationDirPath, stripComponents);
}
else {
throw new UnsupportedOperationException(
"Unsupported format for " + fileName);
}
}
public static void zip(
Path sourcePath, final File zipFile, boolean includeFolder)
throws Exception {
final Path parentPath;
if (includeFolder) {
parentPath = sourcePath.getParent();
}
else {
parentPath = sourcePath;
}
try (FileSystem fileSystem = _createFileSystem(
zipFile.toPath(), true)) {
Files.walkFileTree(
sourcePath,
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(
Path path, BasicFileAttributes basicFileAttributes)
throws IOException {
Path entryPath = parentPath.relativize(path);
copyFile(
path, fileSystem.getPath(entryPath.toString()));
return FileVisitResult.CONTINUE;
}
});
}
}
private static void _appendTar(
File entryFile, Path entryPath,
TarArchiveOutputStream tarArchiveOutputStream)
throws IOException {
TarArchiveEntry tarArchiveEntry = new TarArchiveEntry(
entryPath.toFile());
tarArchiveEntry.setSize(entryFile.length());
tarArchiveOutputStream.putArchiveEntry(tarArchiveEntry);
IOUtils.copy(new FileInputStream(entryFile), tarArchiveOutputStream);
tarArchiveOutputStream.closeArchiveEntry();
}
private static void _appendZip(
File entryFile, Path entryPath, FileSystem fileSystem)
throws IOException {
Path zipPath = fileSystem.getPath(entryPath.toString());
copyFile(entryFile.toPath(), zipPath);
}
private static void _checkResponseStatus(HttpResponse httpResponse)
throws IOException {
StatusLine statusLine = httpResponse.getStatusLine();
if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
throw new IOException(statusLine.getReasonPhrase());
}
}
private static FileSystem _createFileSystem(Path path, boolean create)
throws Exception {
URI uri = path.toUri();
Map<String, String> properties = new HashMap<>();
properties.put("create", Boolean.toString(create));
properties.put("encoding", StandardCharsets.UTF_8.name());
return FileSystems.newFileSystem(
new URI("jar:" + uri.getScheme(), uri.getPath(), null), properties);
}
private static CloseableHttpClient _getHttpClient(
URI uri, String userName, String password) {
HttpClientBuilder httpClientBuilder = HttpClients.custom();
CredentialsProvider credentialsProvider =
new BasicCredentialsProvider();
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
requestConfigBuilder.setCookieSpec(CookieSpecs.STANDARD);
requestConfigBuilder.setRedirectsEnabled(true);
httpClientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
if ((userName != null) && (password != null)) {
credentialsProvider.setCredentials(
new AuthScope(uri.getHost(), uri.getPort()),
new UsernamePasswordCredentials(userName, password));
}
String scheme = uri.getScheme();
String proxyHost = System.getProperty(scheme + ".proxyHost");
String proxyPort = System.getProperty(scheme + ".proxyPort");
String proxyUser = System.getProperty(scheme + ".proxyUser");
String proxyPassword = System.getProperty(scheme + ".proxyPassword");
if ((proxyHost != null) && (proxyPort != null) && (proxyUser != null) &&
(proxyPassword != null)) {
credentialsProvider.setCredentials(
new AuthScope(proxyHost, Integer.parseInt(proxyPort)),
new UsernamePasswordCredentials(proxyUser, proxyPassword));
}
httpClientBuilder.useSystemProperties();
return httpClientBuilder.build();
}
private static void _untar(
Path tarPath, Path destinationDirPath, int stripComponents)
throws IOException {
try (InputStream inputStream = Files.newInputStream(tarPath);
TarArchiveInputStream tarArchiveInputStream =
new TarArchiveInputStream(
new GzipCompressorInputStream(inputStream))) {
TarArchiveEntry tarArchiveEntry = null;
while ((tarArchiveEntry =
tarArchiveInputStream.getNextTarEntry()) != null) {
if (tarArchiveEntry.isDirectory()) {
continue;
}
Path destinationPath = Paths.get(tarArchiveEntry.getName());
destinationPath = destinationDirPath.resolve(
destinationPath.subpath(
stripComponents, destinationPath.getNameCount()));
Files.createDirectories(destinationPath.getParent());
Files.copy(tarArchiveInputStream, destinationPath);
Date lastModifiedDate = tarArchiveEntry.getLastModifiedDate();
Files.setLastModifiedTime(
destinationPath,
FileTime.fromMillis(lastModifiedDate.getTime()));
}
}
}
private static void _unzip(
Path zipPath, final Path destinationDirPath,
final int stripComponents)
throws Exception {
try (FileSystem fileSystem = _createFileSystem(zipPath, false)) {
Files.walkFileTree(
fileSystem.getPath("/"),
new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(
Path path, BasicFileAttributes basicFileAttributes)
throws IOException {
Path relativePath = path.subpath(
stripComponents, path.getNameCount());
Path destinationPath = destinationDirPath.resolve(
relativePath.toString());
copyFile(path, destinationPath);
return FileVisitResult.CONTINUE;
}
});
}
}
private static final long _FILE_LENGTH_KB = 1024;
private static final long _FILE_LENGTH_MB = 1024 * 1024;
}