/**
   * Sends a unicast message to the target defined by msg.getDest() and returns a future
   *
   * @param msg The unicast message to be sent. msg.getDest() must not be null
   * @param options
   * @param listener A FutureListener which will be registered (if non null) with the future
   *     <em>before</em> the call is invoked
   * @return NotifyingFuture<T> A future from which the result can be fetched
   * @throws Exception If there was problem sending the request, processing it at the receiver, or
   *     processing it at the sender. {@link java.util.concurrent.Future#get()} will throw this
   *     exception
   * @throws TimeoutException If the call didn't succeed within the timeout defined in options (if
   *     set)
   */
  public <T> NotifyingFuture<T> sendMessageWithFuture(
      Message msg, RequestOptions options, FutureListener<T> listener) throws Exception {
    Address dest = msg.getDest();
    if (dest == null)
      throw new IllegalArgumentException("message destination is null, cannot send message");

    if (options != null) {
      msg.setFlag(options.getFlags()).setTransientFlag(options.getTransientFlags());
      if (options.getScope() > 0) msg.setScope(options.getScope());
      if (options.getMode() == ResponseMode.GET_NONE) async_unicasts.incrementAndGet();
      else sync_unicasts.incrementAndGet();
    }

    UnicastRequest<T> req = new UnicastRequest<T>(msg, corr, dest, options);
    if (listener != null) req.setListener(listener);
    req.setBlockForResults(false);
    req.execute();
    if (options != null && options.getMode() == ResponseMode.GET_NONE)
      return new NullFuture<T>(null);
    return req;
  }