/** {@inheritDoc} */
  @Override
  public void close(boolean force) {
    assert io != null;

    io.removeEventListener(this);

    if (force) io.forceClose();
    else io.release();
  }
  /**
   * Constructor.
   *
   * @param host Host.
   * @param port Port.
   * @param grid Grid name.
   * @param igfs IGFS name.
   * @param shmem Shared memory flag.
   * @param log Client logger.
   * @throws IOException If failed.
   */
  private HadoopIgfsOutProc(
      String host, int port, String grid, String igfs, boolean shmem, Log log, String user)
      throws IOException {
    assert host != null && !shmem || host == null && shmem
        : "Invalid arguments [host=" + host + ", port=" + port + ", shmem=" + shmem + ']';

    String endpoint = host != null ? host + ":" + port : "shmem:" + port;

    this.grid = grid;
    this.igfs = igfs;
    this.log = log;
    this.userName = IgfsUtils.fixUserName(user);

    io = HadoopIgfsIpcIo.get(log, endpoint);

    io.addEventListener(this);
  }
  /** {@inheritDoc} */
  @Override
  public IgfsFile info(IgfsPath path) throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(INFO);
    msg.path(path);
    msg.userName(userName);

    return io.send(msg).chain(FILE_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public IgfsPathSummary contentSummary(IgfsPath path) throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(PATH_SUMMARY);
    msg.path(path);
    msg.userName(userName);

    return io.send(msg).chain(SUMMARY_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public Collection<IgfsPath> listPaths(IgfsPath path) throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(LIST_PATHS);
    msg.path(path);
    msg.userName(userName);

    return io.send(msg).chain(PATH_COL_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public IgfsHandshakeResponse handshake(String logDir) throws IgniteCheckedException {
    final IgfsHandshakeRequest req = new IgfsHandshakeRequest();

    req.gridName(grid);
    req.igfsName(igfs);
    req.logDirectory(logDir);

    return io.send(req).chain(HANDSHAKE_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public Boolean mkdirs(IgfsPath path, Map<String, String> props) throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(MAKE_DIRECTORIES);
    msg.path(path);
    msg.properties(props);
    msg.userName(userName);

    return io.send(msg).chain(BOOL_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public Boolean delete(IgfsPath path, boolean recursive) throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(DELETE);
    msg.path(path);
    msg.flag(recursive);
    msg.userName(userName);

    return io.send(msg).chain(BOOL_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public Boolean rename(IgfsPath src, IgfsPath dest) throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(RENAME);
    msg.path(src);
    msg.destinationPath(dest);
    msg.userName(userName);

    return io.send(msg).chain(BOOL_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public IgfsFile update(IgfsPath path, Map<String, String> props) throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(UPDATE);
    msg.path(path);
    msg.properties(props);
    msg.userName(userName);

    return io.send(msg).chain(FILE_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public void closeStream(HadoopIgfsStreamDelegate desc) throws IOException {
    final IgfsStreamControlRequest msg = new IgfsStreamControlRequest();

    msg.command(CLOSE);
    msg.streamId((long) desc.target());

    try {
      io.send(msg).chain(BOOL_RES).get();
    } catch (IgniteCheckedException e) {
      throw HadoopIgfsUtils.cast(e);
    }
  }
  /** {@inheritDoc} */
  @Override
  public HadoopIgfsStreamDelegate open(IgfsPath path) throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(OPEN_READ);
    msg.path(path);
    msg.flag(false);
    msg.userName(userName);

    IgfsInputStreamDescriptor rmtDesc = io.send(msg).chain(STREAM_DESCRIPTOR_RES).get();

    return new HadoopIgfsStreamDelegate(this, rmtDesc.streamId(), rmtDesc.length());
  }
  /** {@inheritDoc} */
  @Override
  public Collection<IgfsBlockLocation> affinity(IgfsPath path, long start, long len)
      throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(AFFINITY);
    msg.path(path);
    msg.start(start);
    msg.length(len);
    msg.userName(userName);

    return io.send(msg).chain(BLOCK_LOCATION_COL_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public Boolean setTimes(IgfsPath path, long accessTime, long modificationTime)
      throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(SET_TIMES);
    msg.path(path);
    msg.accessTime(accessTime);
    msg.modificationTime(modificationTime);
    msg.userName(userName);

    return io.send(msg).chain(BOOL_RES).get();
  }
  /** {@inheritDoc} */
  @Override
  public HadoopIgfsStreamDelegate append(
      IgfsPath path, boolean create, @Nullable Map<String, String> props)
      throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(OPEN_APPEND);
    msg.path(path);
    msg.flag(create);
    msg.properties(props);
    msg.userName(userName);

    Long streamId = io.send(msg).chain(LONG_RES).get();

    return new HadoopIgfsStreamDelegate(this, streamId);
  }
  /** {@inheritDoc} */
  @Override
  public void writeData(HadoopIgfsStreamDelegate desc, byte[] data, int off, int len)
      throws IOException {
    final IgfsStreamControlRequest msg = new IgfsStreamControlRequest();

    msg.command(WRITE_BLOCK);
    msg.streamId((long) desc.target());
    msg.data(data);
    msg.position(off);
    msg.length(len);

    try {
      io.sendPlain(msg);
    } catch (IgniteCheckedException e) {
      throw HadoopIgfsUtils.cast(e);
    }
  }
  /** {@inheritDoc} */
  @Override
  public IgniteInternalFuture<byte[]> readData(
      HadoopIgfsStreamDelegate desc,
      long pos,
      int len,
      final @Nullable byte[] outBuf,
      final int outOff,
      final int outLen) {
    assert len > 0;

    final IgfsStreamControlRequest msg = new IgfsStreamControlRequest();

    msg.command(READ_BLOCK);
    msg.streamId((long) desc.target());
    msg.position(pos);
    msg.length(len);

    try {
      return io.send(msg, outBuf, outOff, outLen);
    } catch (IgniteCheckedException e) {
      return new GridFinishedFuture<>(e);
    }
  }
  /** {@inheritDoc} */
  @Override
  public HadoopIgfsStreamDelegate create(
      IgfsPath path,
      boolean overwrite,
      boolean colocate,
      int replication,
      long blockSize,
      @Nullable Map<String, String> props)
      throws IgniteCheckedException {
    final IgfsPathControlRequest msg = new IgfsPathControlRequest();

    msg.command(OPEN_CREATE);
    msg.path(path);
    msg.flag(overwrite);
    msg.colocate(colocate);
    msg.properties(props);
    msg.replication(replication);
    msg.blockSize(blockSize);
    msg.userName(userName);

    Long streamId = io.send(msg).chain(LONG_RES).get();

    return new HadoopIgfsStreamDelegate(this, streamId);
  }
 /** {@inheritDoc} */
 @Override
 public IgfsStatus fsStatus() throws IgniteCheckedException {
   return io.send(new IgfsStatusRequest()).chain(STATUS_RES).get();
 }