/** * 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)); }