/**
* 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.sync.engine.util;
import com.liferay.petra.io.delta.ByteChannelReader;
import com.liferay.petra.io.delta.ByteChannelWriter;
import com.liferay.petra.io.delta.DeltaUtil;
import com.liferay.sync.engine.model.SyncFile;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Shinn Lok
*/
public class IODeltaUtil {
public static Path checksums(SyncFile syncFile) {
if (isIgnoredFilePatchingExtension(syncFile)) {
return null;
}
Path syncFilePath = Paths.get(syncFile.getFilePathName());
if (Files.isDirectory(syncFilePath) ||
FileUtil.notExists(syncFilePath)) {
return null;
}
FileChannel fileChannel = null;
WritableByteChannel writableByteChannel = null;
try {
fileChannel = FileChannel.open(syncFilePath);
Path checksumsFilePath = getChecksumsFilePath(syncFile);
if (FileUtil.notExists(checksumsFilePath)) {
Files.createFile(checksumsFilePath);
}
writableByteChannel = Files.newByteChannel(
checksumsFilePath, StandardOpenOption.WRITE);
ByteChannelWriter byteChannelWriter = new ByteChannelWriter(
writableByteChannel);
DeltaUtil.checksums(fileChannel, byteChannelWriter);
byteChannelWriter.finish();
return checksumsFilePath;
}
catch (IOException ioe) {
_logger.error(ioe.getMessage(), ioe);
return null;
}
finally {
StreamUtil.cleanUp(fileChannel);
StreamUtil.cleanUp(writableByteChannel);
}
}
public static Path copyChecksums(
SyncFile sourceSyncFile, SyncFile targetSyncFile) {
if (isIgnoredFilePatchingExtension(sourceSyncFile) ||
isIgnoredFilePatchingExtension(targetSyncFile)) {
return null;
}
try {
Path sourceChecksumsFilePath = getChecksumsFilePath(sourceSyncFile);
if (FileUtil.notExists(sourceChecksumsFilePath)) {
checksums(sourceSyncFile);
}
Path targetChecksumsFilePath = getChecksumsFilePath(targetSyncFile);
Files.copy(
sourceChecksumsFilePath, targetChecksumsFilePath,
StandardCopyOption.REPLACE_EXISTING);
return targetChecksumsFilePath;
}
catch (IOException ioe) {
_logger.error(ioe.getMessage(), ioe);
return null;
}
}
public static Path delta(
Path targetFilePath, Path checksumsFilePath, Path deltaFilePath) {
if (FileUtil.notExists(targetFilePath) ||
FileUtil.notExists(checksumsFilePath) ||
FileUtil.notExists(deltaFilePath)) {
return null;
}
ReadableByteChannel targetReadableByteChannel = null;
ReadableByteChannel checksumsReadableByteChannel = null;
WritableByteChannel deltaWritableByteChannel = null;
try {
targetReadableByteChannel = Files.newByteChannel(
targetFilePath, StandardOpenOption.READ);
checksumsReadableByteChannel = Files.newByteChannel(
checksumsFilePath, StandardOpenOption.READ);
ByteChannelReader checksumsByteChannelReader =
new ByteChannelReader(checksumsReadableByteChannel);
deltaWritableByteChannel = Files.newByteChannel(
deltaFilePath, StandardOpenOption.WRITE);
ByteChannelWriter deltaByteChannelWriter = new ByteChannelWriter(
deltaWritableByteChannel);
DeltaUtil.delta(
targetReadableByteChannel, checksumsByteChannelReader,
deltaByteChannelWriter);
deltaByteChannelWriter.finish();
return deltaFilePath;
}
catch (IOException ioe) {
_logger.error(ioe.getMessage(), ioe);
return null;
}
finally {
StreamUtil.cleanUp(targetReadableByteChannel);
StreamUtil.cleanUp(checksumsReadableByteChannel);
StreamUtil.cleanUp(deltaWritableByteChannel);
}
}
public static Path getChecksumsFilePath(SyncFile syncFile) {
return FileUtil.getFilePath(
PropsValues.SYNC_CONFIGURATION_DIRECTORY, "files",
String.valueOf(syncFile.getSyncFileId()));
}
public static boolean isIgnoredFilePatchingExtension(SyncFile syncFile) {
return _syncFilePatchingIgnoreFileExtensions.contains(
syncFile.getExtension());
}
public static Path patch(
Path targetFilePath, InputStream deltaInputStream) {
if (FileUtil.notExists(targetFilePath)) {
return null;
}
FileInputStream targetInputStream = null;
FileChannel targetFileChannel = null;
Path patchedFilePath = null;
WritableByteChannel patchedWritableByteChannel = null;
ReadableByteChannel deltaReadableByteChannel = null;
try {
targetInputStream = new FileInputStream(targetFilePath.toString());
targetFileChannel = targetInputStream.getChannel();
patchedFilePath = Files.createTempFile(
String.valueOf(targetFilePath.getFileName()), ".tmp");
patchedWritableByteChannel = Files.newByteChannel(
patchedFilePath, StandardOpenOption.WRITE);
deltaReadableByteChannel = Channels.newChannel(deltaInputStream);
ByteChannelReader deltaByteChannelReader = new ByteChannelReader(
deltaReadableByteChannel);
DeltaUtil.patch(
targetFileChannel, patchedWritableByteChannel,
deltaByteChannelReader);
}
catch (IOException ioe) {
_logger.error(ioe.getMessage(), ioe);
return null;
}
finally {
StreamUtil.cleanUp(targetInputStream);
StreamUtil.cleanUp(targetFileChannel);
StreamUtil.cleanUp(patchedWritableByteChannel);
StreamUtil.cleanUp(deltaReadableByteChannel);
}
try {
// Workaround for JDK-8150700
if (OSDetector.isWindows()) {
Files.delete(targetFilePath);
}
Files.move(
patchedFilePath, targetFilePath,
StandardCopyOption.REPLACE_EXISTING);
return targetFilePath;
}
catch (IOException ioe) {
_logger.error(ioe.getMessage(), ioe);
return null;
}
}
private static final Logger _logger = LoggerFactory.getLogger(
IODeltaUtil.class);
private static final Set<String> _syncFilePatchingIgnoreFileExtensions =
new HashSet<>(
Arrays.asList(
PropsValues.SYNC_FILE_PATCHING_IGNORE_FILE_EXTENSIONS));
}