/**
* 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.lan.server.file;
import com.liferay.sync.engine.util.OSDetector;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.stream.ChunkedInput;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
/**
* @author Dennis Ju
*/
public class SyncChunkedFile implements ChunkedInput<ByteBuf> {
public SyncChunkedFile(
Path path, long length, int chunkSize, long modifiedTime)
throws IOException {
this(path, 0L, length, chunkSize, modifiedTime);
}
public SyncChunkedFile(
Path path, long offset, long length, int chunkSize,
long modifiedTime)
throws IOException {
if (offset != 0L) {
_fileChannel.position(offset);
}
_path = path;
_offset = offset;
_chunkSize = chunkSize;
_modifiedTime = modifiedTime;
_startOffset = offset;
_endOffset = offset + length;
if (OSDetector.isWindows()) {
_closeAggressively = true;
}
else {
_closeAggressively = false;
}
}
@Override
public void close() throws Exception {
if (_fileChannel != null) {
_fileChannel.close();
}
_closed = true;
}
@Override
public boolean isEndOfInput() throws Exception {
if ((_offset >= _endOffset) || _closed) {
return true;
}
return false;
}
@Override
public long length() {
return _endOffset - _startOffset;
}
@Override
public long progress() {
return _offset - _startOffset;
}
@Override
public ByteBuf readChunk(ByteBufAllocator byteBufAllocator)
throws Exception {
long offset = _offset;
if (offset >= _endOffset) {
return null;
}
int chunkSize = (int)Math.min((long)_chunkSize, _endOffset - offset);
ByteBuf byteBuf = byteBufAllocator.buffer(chunkSize);
boolean release = true;
try {
FileTime currentFileTime = Files.getLastModifiedTime(
_path, LinkOption.NOFOLLOW_LINKS);
long currentTime = currentFileTime.toMillis();
if (currentTime != _modifiedTime) {
throw new Exception("File modified during transfer: " + _path);
}
int bytesRead = 0;
if (_closeAggressively || (_fileChannel == null)) {
_fileChannel = FileChannel.open(_path);
_fileChannel.position(_offset);
}
while (true) {
int localBytesRead = byteBuf.writeBytes(
_fileChannel, chunkSize - bytesRead);
if (localBytesRead >= 0) {
bytesRead += localBytesRead;
if (bytesRead != chunkSize) {
continue;
}
}
_offset += bytesRead;
release = false;
return byteBuf;
}
}
finally {
if (_closeAggressively && (_fileChannel != null)) {
_fileChannel.close();
}
if (release) {
byteBuf.release();
}
}
}
/**
* @deprecated As of 3.3.0, As of Netty 4.1.0, replaced by {@link
* #readChunk(ByteBufAllocator)}
*/
@Deprecated
@Override
public ByteBuf readChunk(ChannelHandlerContext channelHandlerContext)
throws Exception {
return readChunk(channelHandlerContext.alloc());
}
private final int _chunkSize;
private final boolean _closeAggressively;
private boolean _closed;
private final long _endOffset;
private FileChannel _fileChannel;
private final long _modifiedTime;
private long _offset;
private final Path _path;
private final long _startOffset;
}