org.dcache.xrootd.tpc.TpcSourceReadHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.dcache.xrootd.tpc.TpcSourceReadHandler.java

Source

/**
 * Copyright (C) 2011-2019 dCache.org <support@dcache.org>
 *
 * This file is part of xrootd4j.
 *
 * xrootd4j 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 3 of the License, or
 * (at your option) any later version.
 *
 * xrootd4j 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with xrootd4j.  If not, see http://www.gnu.org/licenses/.
 */
package org.dcache.xrootd.tpc;

import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;

import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.TimeUnit;

import org.dcache.xrootd.core.XrootdException;
import org.dcache.xrootd.tpc.protocol.messages.AbstractXrootdInboundResponse;
import org.dcache.xrootd.tpc.protocol.messages.InboundAttnResponse;
import org.dcache.xrootd.tpc.protocol.messages.InboundChecksumResponse;
import org.dcache.xrootd.tpc.protocol.messages.InboundReadResponse;
import org.dcache.xrootd.tpc.protocol.messages.OutboundChecksumRequest;
import org.dcache.xrootd.tpc.protocol.messages.OutboundReadRequest;

import static io.netty.channel.ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE;
import static org.dcache.xrootd.protocol.XrootdProtocol.*;

/**
 * <p>This handler reads until the file is complete, terminating the session
 *      thereafter.  When complete, it uses the write handler on its client to
 *      send a reply to the kXR_sync request received from the initiating client,
 *      and calls back to disconnect the third-party client.</p>
 *
 * <p>Optional checksum verification (done prior to the sync reply)
 *    is implemented by subclasses.</p>
 */
public abstract class TpcSourceReadHandler extends AbstractClientSourceHandler {
    @Override
    protected void doOnAsynResponse(ChannelHandlerContext ctx, InboundAttnResponse response)
            throws XrootdException {
        switch (response.getRequestId()) {
        case kXR_read:
            sendReadRequest(ctx);
            break;
        case kXR_query:
            sendChecksumRequest(ctx);
            break;
        default:
            super.doOnAsynResponse(ctx, response);
        }
    }

    @Override
    protected void doOnChecksumResponse(ChannelHandlerContext ctx, InboundChecksumResponse response)
            throws XrootdException {
        int status = response.getStatus();
        XrootdTpcInfo tpcInfo = client.getInfo();
        LOGGER.trace("Checksum query response for {} on {}, channel {}, stream {} " + "received, status {}.",
                tpcInfo.getLfn(), tpcInfo.getSrc(), ctx.channel().id(), client.getStreamId(), status);
        if (status != kXR_ok) {
            String error = String.format("Checksum query for %s failed.", tpcInfo.getLfn());
            handleTransferTerminated(status, error, ctx);
            return;
        }

        validateChecksum(response, ctx);
    }

    /*
     * TODO: revisit read implementation to see if parallel read is feasible.
     */
    @Override
    protected void doOnReadResponse(ChannelHandlerContext ctx, InboundReadResponse response) {
        try {
            int status = response.getStatus();
            XrootdTpcInfo tpcInfo = client.getInfo();
            int bytesRcvd = response.getDlen();
            LOGGER.trace(
                    "Read response received for {} on {}, channel {}, " + "stream {}: status {}, "
                            + "got {} more bytes.",
                    tpcInfo.getLfn(), tpcInfo.getSrc(), ctx.channel().id(), client.getStreamId(), status,
                    bytesRcvd);

            if (status != kXR_ok && status != kXR_oksofar) {
                String error = String.format("Read of %s failed with status %s.", tpcInfo.getLfn(), status);
                handleTransferTerminated(kXR_error, error, ctx);
                return;
            }

            long writeOffset = client.getWriteOffset();

            if (bytesRcvd > 0) {
                try {
                    response.setWriteOffset(writeOffset);
                    client.getWriteHandler().write(response);
                    writeOffset += bytesRcvd;
                    client.setWriteOffset(writeOffset);
                } catch (ClosedChannelException e) {
                    handleTransferTerminated(kXR_ServerError,
                            "Channel " + ctx.channel().id() + " was forcefully " + "closed by the server.", ctx);
                    return;
                } catch (IOException e) {
                    handleTransferTerminated(kXR_IOError, e.toString(), ctx);
                    return;
                }

                LOGGER.trace("Read of {} on {}, channel {}, stream {}: " + "wrote {}, " + "so far {}, expected {}.",
                        tpcInfo.getLfn(), tpcInfo.getSrc(), ctx.channel().id(), client.getStreamId(), bytesRcvd,
                        writeOffset, tpcInfo.getAsize());
            }

            if (status == kXR_oksofar) {
                LOGGER.trace("Waiting for more data for {} on {}, " + "channel {}, stream {}", tpcInfo.getLfn(),
                        tpcInfo.getSrc(), ctx.channel().id(), client.getStreamId());
                return;
            }

            if (writeOffset < tpcInfo.getAsize()) {
                sendReadRequest(ctx);
            } else if (tpcInfo.getCks() != null) {
                sendChecksumRequest(ctx);
            } else {
                LOGGER.trace(
                        "Read for {} on {}, channel {}, stream {}," + " completed without "
                                + "checksum verification.",
                        tpcInfo.getLfn(), tpcInfo.getSrc(), ctx.channel().id(), client.getStreamId());
                handleTransferTerminated(kXR_ok, null, ctx);
            }
        } finally {
            ReferenceCountUtil.release(response);
        }
    }

    @Override
    protected void doOnWaitResponse(final ChannelHandlerContext ctx, AbstractXrootdInboundResponse response)
            throws XrootdException {
        switch (response.getRequestId()) {
        case kXR_read:
            client.getExecutor().schedule(() -> {
                sendReadRequest(ctx);
            }, getWaitInSeconds(response), TimeUnit.SECONDS);
            break;
        case kXR_query:
            client.getExecutor().schedule(() -> {
                sendChecksumRequest(ctx);
            }, getWaitInSeconds(response), TimeUnit.SECONDS);
            break;
        default:
            super.doOnWaitResponse(ctx, response);
        }
    }

    protected void handleTransferTerminated(int status, String error, ChannelHandlerContext ctx) {
        client.getWriteHandler().fireDelayedSync(status, error);
        LOGGER.trace("handleTransferTerminated called fire delayed sync, " + "calling client shutdown");
        try {
            client.shutDown(ctx);
        } catch (InterruptedException e) {
            LOGGER.warn("Client shutdown interrupted.");
        }
    }

    @Override
    protected void sendReadRequest(ChannelHandlerContext ctx) {
        XrootdTpcInfo tpcInfo = client.getInfo();
        LOGGER.trace("sendReadRequest to {}, channel {}, stream {}, " + "fhandle {}, offset {}, chunksize {}.",
                tpcInfo.getSrc(), ctx.channel().id(), client.getStreamId(), client.getFhandle(),
                client.getWriteOffset(), getChunkSize());
        client.setExpectedResponse(kXR_read);

        ctx.writeAndFlush(new OutboundReadRequest(client.getStreamId(), client.getFhandle(),
                client.getWriteOffset(), getChunkSize()), ctx.newPromise()).addListener(FIRE_EXCEPTION_ON_FAILURE);
    }

    @Override
    protected void sendChecksumRequest(ChannelHandlerContext ctx) {
        XrootdTpcInfo tpcInfo = client.getInfo();
        LOGGER.trace("sendChecksumRequest to {}, channel {}, " + "stream {}, fhandle {}.", tpcInfo.getSrc(),
                ctx.channel().id(), client.getStreamId(), client.getFhandle());
        client.setExpectedResponse(kXR_query);
        ctx.writeAndFlush(new OutboundChecksumRequest(client.getStreamId(), tpcInfo.getLfn()), ctx.newPromise())
                .addListener(FIRE_EXCEPTION_ON_FAILURE);
    }

    protected abstract void validateChecksum(InboundChecksumResponse response, ChannelHandlerContext ctx)
            throws XrootdException;

    protected abstract int getChunkSize();
}