org.dcache.http.ReusableChunkedNioFile.java Source code

Java tutorial

Introduction

Here is the source code for org.dcache.http.ReusableChunkedNioFile.java

Source

package org.dcache.http;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.stream.ChunkedInput;
import io.netty.handler.stream.ChunkedNioFile;

import java.nio.ByteBuffer;

import org.dcache.pool.repository.RepositoryChannel;

/*
 * Portions of this file based on Netty's ChunkedNioFile which has the
 * following copyright:
 *
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
*/
public class ReusableChunkedNioFile implements ChunkedInput<ByteBuf> {
    private final RepositoryChannel _channel;
    private final long _startOffset;
    private final long _endOffset;
    private final int _chunkSize;

    private volatile long _offset;

    public ReusableChunkedNioFile(RepositoryChannel channel, long offset, long length, int chunkSize) {
        if (channel == null) {
            throw new NullPointerException("Channel must not be null");
        }

        if (offset < 0) {
            throw new IllegalArgumentException("offset: " + offset + " (expected: 0 or greater)");
        }

        if (length < 0) {
            throw new IllegalArgumentException("length: " + length + " (expected: 0 or greater)");
        }

        if (chunkSize <= 0) {
            throw new IllegalArgumentException("chunkSize: " + chunkSize + " (expected: 1 or greater)");
        }

        _channel = channel;
        _chunkSize = chunkSize;
        _startOffset = _offset = offset;
        _endOffset = _offset + length;
    }

    /**
     * With a normal ChunkedNioFile, Netty at some point receives a
     * "connection closed by peer" signal and closes the file, trying to
     * release the resources. As this closes the disk-file, the mover becomes
     * useless despite keep-alive. To avoid this, close here is a no-op.
     */
    @Override
    public void close() throws Exception {
        /* make sure to close the backing stream yourself */
    }

    @Override
    public boolean isEndOfInput() throws Exception {
        return _offset >= _endOffset || !_channel.isOpen();
    }

    @Override
    public ByteBuf readChunk(ChannelHandlerContext ctx) throws Exception {
        return readChunk(ctx.alloc());
    }

    /**
     * Like {@link ChunkedNioFile#readChunk}, but uses position independent
     * IO calls.
     */
    @Override
    public ByteBuf readChunk(ByteBufAllocator allocator) throws Exception {
        long offset = _offset;
        if (offset >= _endOffset) {
            return null;
        }

        int length = (int) Math.min(_chunkSize, _endOffset - offset);
        ByteBuf chunk = allocator.buffer(length);
        boolean release = true;
        try {
            ByteBuffer buffer = chunk.nioBuffer(0, length);

            while (buffer.hasRemaining()) {
                /* use position independent thread safe call */
                int bytes = _channel.read(buffer, offset);
                if (bytes < 0) {
                    break;
                }
                offset += bytes;
            }
            chunk.writerIndex(buffer.position());
            _offset = offset;
            release = false;
            return chunk;
        } finally {
            if (release) {
                chunk.release();
            }
        }
    }

    @Override
    public long length() {
        return _endOffset - _startOffset;
    }

    @Override
    public long progress() {
        return _offset - _startOffset;
    }

    /**
     * Returns the repository channel. Used for unit testing.
     */
    RepositoryChannel getChannel() {
        return _channel;
    }

    /**
     * Returns the end offset. Used for unit testing.
     */
    long getEndOffset() {
        return _endOffset;
    }

    /**
     * Returns the current offset. Used for unit testing.
     */
    long getOffset() {
        return _offset;
    }
}