private static void ensureThatExceptionHandlerIsLast(ChannelPipeline pipeline) {
    ChannelInboundHandler exceptionHandler = ChannelExceptionHandler.getInstance();
    if (pipeline.last() != exceptionHandler || pipeline.context(exceptionHandler) == null) {
      return;
    }

    pipeline.remove(exceptionHandler);
    pipeline.addLast(exceptionHandler);
  }
  /**
   * Creates an instance of {@link NettyPacketReader}. If this is used to read a block remotely, it
   * requires the block to be locked beforehand and the lock ID is passed to this class.
   *
   * @param context the file system context
   * @param address the netty data server network address
   * @param id the block ID or UFS file ID
   * @param offset the offset
   * @param len the length to read
   * @param lockId the lock ID
   * @param sessionId the session ID
   * @param type the request type (block or UFS file)
   * @throws IOException if it fails to acquire a netty channel
   */
  private NettyPacketReader(
      FileSystemContext context,
      InetSocketAddress address,
      long id,
      long offset,
      long len,
      long lockId,
      long sessionId,
      Protocol.RequestType type)
      throws IOException {
    Preconditions.checkArgument(offset >= 0 && len > 0);

    mContext = context;
    mAddress = address;
    mId = id;
    mStart = offset;
    mPosToRead = offset;
    mBytesToRead = len;
    mRequestType = type;

    mChannel = context.acquireNettyChannel(address);

    ChannelPipeline pipeline = mChannel.pipeline();
    if (!(pipeline.last() instanceof RPCMessageDecoder)) {
      throw new RuntimeException(
          String.format(
              "Channel pipeline has unexpected handlers %s.",
              pipeline.last().getClass().getCanonicalName()));
    }
    mChannel.pipeline().addLast(new PacketReadHandler());

    Protocol.ReadRequest readRequest =
        Protocol.ReadRequest.newBuilder()
            .setId(id)
            .setOffset(offset)
            .setLength(len)
            .setLockId(lockId)
            .setSessionId(sessionId)
            .setType(type)
            .build();
    mChannel
        .writeAndFlush(new RPCProtoMessage(readRequest))
        .addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
  }